Fotogalerie se zoomem, uprava verejnych avataru
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
import {boot} from "quasar/wrappers";
|
||||
import InnerImageZoom from 'vue-inner-image-zoom';
|
||||
|
||||
export default boot(({app}) => {
|
||||
app.component("inner-image-zoom", InnerImageZoom);
|
||||
})
|
||||
@@ -0,0 +1,20 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 120
|
||||
},
|
||||
src: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-avatar :size="`${size}px`">
|
||||
<div :style="`width: ${size}px; height: ${size}px; background-image: url('${src}'); background-size: cover`">
|
||||
</div>
|
||||
</q-avatar>
|
||||
</template>
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import {flight} from "src/composables/objects/flight";
|
||||
import {formatDatetime, minutesToHumanReadable} from "src/composables/datetimeUtils";
|
||||
import {FORMAT_DATE, FORMAT_TIME, formatDatetime, minutesToHumanReadable} from "src/composables/datetimeUtils";
|
||||
import WeatherInfo from "components/weather/WeatherInfo.vue";
|
||||
|
||||
const props = defineProps({
|
||||
@@ -14,7 +14,15 @@ const props = defineProps({
|
||||
<template>
|
||||
<q-list bordered separator>
|
||||
<q-item>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="event"/>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
{{ formatDatetime(flight.takeoffDatetime, FORMAT_DATE) }}
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item>
|
||||
<q-list>
|
||||
<q-item>
|
||||
<q-item-section avatar>
|
||||
@@ -22,7 +30,8 @@ const props = defineProps({
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<div>
|
||||
{{ flight.takeoffAirport.name }}
|
||||
<strong>{{ flight.takeoffAirport.name }}</strong>,
|
||||
čas vzletu: {{ formatDatetime(flight.takeoffDatetime, FORMAT_TIME) }}
|
||||
|
||||
<WeatherInfo class="q-mx-sm" v-if="flight.takeoffWeatherInfo" :weather="flight.takeoffWeatherInfo"/>
|
||||
</div>
|
||||
@@ -44,7 +53,8 @@ const props = defineProps({
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<div>
|
||||
{{ flight.landingAirport.name }}
|
||||
<strong>{{ flight.landingAirport.name }}</strong>,
|
||||
čas přistání: {{ formatDatetime(flight.landingDatetime, FORMAT_TIME) }}
|
||||
<WeatherInfo class="q-mx-sm" v-if="flight.landingWeatherInfo" :weather="flight.landingWeatherInfo"/>
|
||||
</div>
|
||||
</q-item-section>
|
||||
@@ -52,15 +62,6 @@ const props = defineProps({
|
||||
</q-list>
|
||||
</q-item>
|
||||
|
||||
|
||||
<q-item>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="event"/>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
{{ formatDatetime(flight.takeoffDatetime) }}
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="schedule"/>
|
||||
@@ -74,6 +75,7 @@ const props = defineProps({
|
||||
<q-icon name="people"/>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
Link jen pro prihlasene!
|
||||
<router-link :to="{name: 'copilotDetail', params: {id: flight.copilot.id}}">
|
||||
{{ flight.copilot.name }}
|
||||
</router-link>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script setup>
|
||||
import {formatDatetime, minutesToHumanReadable} from "src/composables/datetimeUtils";
|
||||
import {FORMAT_DATE, formatDatetime, minutesToHumanReadable} from "src/composables/datetimeUtils";
|
||||
import {computed} from "vue";
|
||||
import {useQuasar} from "quasar";
|
||||
import {useFlightsStore} from "stores/Flights";
|
||||
import {flightName} from "../../composables/objects/flight";
|
||||
import AvatarFromPhoto from "components/AvatarFromPhoto.vue";
|
||||
|
||||
const props = defineProps({
|
||||
items: {
|
||||
@@ -33,15 +34,13 @@ const props = defineProps({
|
||||
:to="{name: routeDetail.name, params:{...routeDetail.params, id: flight.id}}"
|
||||
>
|
||||
<q-item-section avatar>
|
||||
<q-avatar size="150px">
|
||||
<div v-if="flight.coverPhoto" :style="`width: 150px; height: 150px; background-image: url('${flight.coverPhoto.thumbnailUrl}'); background-size: cover`"></div>
|
||||
</q-avatar>
|
||||
<AvatarFromPhoto :size="120" v-if="flight.coverPhoto" :src="flight.coverPhoto.thumbnailUrl" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<h5>{{ flightName(flight) }}</h5>
|
||||
<div class="text-secondary">
|
||||
<div class="text-h5">{{ flightName(flight) }}</div>
|
||||
<div class="text-secondary q-mt-md">
|
||||
<q-icon name="flight_takeoff"/>
|
||||
{{ formatDatetime(flight.takeoffDatetime) }}
|
||||
{{ formatDatetime(flight.takeoffDatetime, FORMAT_DATE) }}
|
||||
</div>
|
||||
<div class="text-secondary">
|
||||
<q-icon name="schedule"/>
|
||||
@@ -52,6 +51,12 @@ const props = defineProps({
|
||||
<q-icon name="flight"/>
|
||||
{{ flight.aircraft.callSign }}
|
||||
</div>
|
||||
|
||||
<div class="text-secondary" v-if="flight.copilot">
|
||||
<q-icon name="person" />
|
||||
{{ flight.copilot.name }}
|
||||
</div>
|
||||
|
||||
</q-item-section>
|
||||
|
||||
<q-item-section side top v-if="flight.isPublic && displayVisibilityInfo">
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import EmbeddedMap from "components/map/EmbeddedMap.vue";
|
||||
import {computed, onMounted, ref} from "vue";
|
||||
import {formatDatetime} from "src/composables/datetimeUtils";
|
||||
import 'vue-inner-image-zoom/lib/vue-inner-image-zoom.css'
|
||||
|
||||
const props = defineProps({
|
||||
photos: {
|
||||
@@ -65,44 +66,41 @@ const showExtra = ref(false);
|
||||
<q-card-section>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<q-img
|
||||
:draggable="false"
|
||||
style="max-height: 90vh"
|
||||
fit="contain"
|
||||
:src="photo.url"
|
||||
<div class="text-center">
|
||||
<inner-image-zoom move-type="pan" :zoomScale="0.75" :hide-hint="true" :src="photo.url"/>
|
||||
</div>
|
||||
|
||||
<q-btn
|
||||
class="absolute all-pointer-events"
|
||||
round
|
||||
flat
|
||||
icon="info"
|
||||
:text-color="showExtra ? 'primary' : 'white'"
|
||||
style="background: rgba(0, 0, 0, 0.25); top: 8px; right: 8px"
|
||||
@click="showExtra = !showExtra"
|
||||
/>
|
||||
<q-btn
|
||||
class="absolute all-pointer-events"
|
||||
style="background: rgba(0, 0, 0, 0.25); top: 50%; left: 8px;"
|
||||
@click="prevPhoto"
|
||||
v-if="photoIndex > 0"
|
||||
icon="chevron_left"
|
||||
text-color="white"
|
||||
flat
|
||||
rounded
|
||||
>
|
||||
<q-btn
|
||||
class="absolute all-pointer-events"
|
||||
round
|
||||
flat
|
||||
icon="info"
|
||||
:text-color="showExtra ? 'primary' : 'white'"
|
||||
style="background: rgba(0, 0, 0, 0.25); top: 8px; right: 8px"
|
||||
@click="showExtra = !showExtra"
|
||||
/>
|
||||
<q-btn
|
||||
class="absolute all-pointer-events"
|
||||
style="background: rgba(0, 0, 0, 0.25); top: 50%; left: 8px;"
|
||||
@click="prevPhoto"
|
||||
v-if="photoIndex > 0"
|
||||
icon="chevron_left"
|
||||
text-color="white"
|
||||
flat
|
||||
rounded
|
||||
>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
class="absolute all-pointer-events"
|
||||
style="background: rgba(0, 0, 0, 0.25); top: 50%; right: 8px;"
|
||||
@click="nextPhoto"
|
||||
v-if="photoIndex < photos.length - 1"
|
||||
icon-right="chevron_right"
|
||||
text-color="white"
|
||||
flat
|
||||
rounded
|
||||
>
|
||||
</q-btn>
|
||||
</q-img>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
class="absolute all-pointer-events"
|
||||
style="background: rgba(0, 0, 0, 0.25); top: 50%; right: 8px;"
|
||||
@click="nextPhoto"
|
||||
v-if="photoIndex < photos.length - 1"
|
||||
icon-right="chevron_right"
|
||||
text-color="white"
|
||||
flat
|
||||
rounded
|
||||
>
|
||||
</q-btn>
|
||||
<strong v-if="photo.name !== ''">{{ photo.name }}</strong>
|
||||
<p v-if="photo.description !== ''">{{ photo.description }}</p>
|
||||
</div>
|
||||
@@ -148,3 +146,15 @@ const showExtra = ref(false);
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.iiz {
|
||||
width: 100%;
|
||||
height: 90vh;
|
||||
}
|
||||
|
||||
.iiz__img {
|
||||
max-height: 90vh !important;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -13,9 +13,6 @@ const props = defineProps({
|
||||
}
|
||||
});
|
||||
const formData = reactive({
|
||||
name: '',
|
||||
publicUsername: '',
|
||||
description: '',
|
||||
avatarImage: null,
|
||||
titleImage: null,
|
||||
});
|
||||
@@ -59,16 +56,6 @@ watch(() => props.user, (newVal) => {
|
||||
<q-icon name="upload"/>
|
||||
</template>
|
||||
</q-file>
|
||||
|
||||
<q-input v-model="formData.name" label="Jméno" v-bind="commonProps"/>
|
||||
<q-input
|
||||
v-model="formData.publicUsername"
|
||||
label="Veřejné uživatelské jméno"
|
||||
hint="Vyplň jméno pro zpřístupnění záznamů ve veřejné části aplikace"
|
||||
v-bind="commonProps"
|
||||
/>
|
||||
|
||||
<RichTextEditor v-model="formData.description" label="Veřejný podpis" />
|
||||
</FormWrapper>
|
||||
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<script setup>
|
||||
import {onMounted} from "vue";
|
||||
import {onMounted, reactive, watch} from "vue";
|
||||
import {useRoute} from "vue-router";
|
||||
import {useUserStore} from "stores/User";
|
||||
import {commonProps} from "src/composables/inputs";
|
||||
import RichTextEditor from "components/inputs/RichTextEditor.vue";
|
||||
import FormWrapper from "components/FormWrapper.vue";
|
||||
import {setDefaultData} from "src/composables/form/formHandling";
|
||||
|
||||
const props = defineProps({
|
||||
user: {
|
||||
@@ -11,11 +15,14 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
const username = useRoute().params.username;
|
||||
|
||||
onMounted(() => {
|
||||
useUserStore().fetchByUsername(username);
|
||||
const formData = reactive({
|
||||
name: '',
|
||||
publicUsername: '',
|
||||
description: '',
|
||||
});
|
||||
watch(() => props.user, (newVal) => {
|
||||
setDefaultData(formData, props.user)
|
||||
}, {immediate: true});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -29,4 +36,16 @@ onMounted(() => {
|
||||
<q-icon name="" color="red" /> Nastav si veřejné uživatelské jméno pro zpřístupnění profilu a sdílení záznamů s ostatními
|
||||
</q-item>
|
||||
</q-list>
|
||||
|
||||
<FormWrapper v-model="formData">
|
||||
<q-input v-model="formData.name" label="Jméno" v-bind="commonProps"/>
|
||||
<q-input
|
||||
v-model="formData.publicUsername"
|
||||
label="Veřejné uživatelské jméno"
|
||||
hint="Vyplň jméno pro zpřístupnění záznamů ve veřejné části aplikace"
|
||||
v-bind="commonProps"
|
||||
/>
|
||||
|
||||
<RichTextEditor v-model="formData.description" label="Veřejný podpis" />
|
||||
</FormWrapper>
|
||||
</template>
|
||||
|
||||
@@ -38,15 +38,6 @@ const openDetail = ref(false);
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item>
|
||||
<q-item-section avatar>
|
||||
QNH
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
{{ weather.qnh }} hPa
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="air"/>
|
||||
@@ -58,14 +49,32 @@ const openDetail = ref(false);
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="water_drop"/>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
{{ weather.rain }}mm
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="mdi-thermometer"/>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
Teplota: {{ weather.temperatureSurface }} ° C
|
||||
Teplota: {{ weather.temperatureSurface }}° C
|
||||
<br>
|
||||
Rosný bod: {{ weather.dewpointSurface }} ° C
|
||||
Rosný bod: {{ weather.dewpointSurface }}° C
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item>
|
||||
<q-item-section avatar>
|
||||
QNH
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
{{ weather.qnh }} hPa
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
|
||||
@@ -91,6 +91,11 @@ fragment flightListInfo on Flight {
|
||||
thumbnailUrl
|
||||
}
|
||||
|
||||
copilot {
|
||||
id
|
||||
name
|
||||
}
|
||||
|
||||
takeoffAirport {
|
||||
...airportInfo
|
||||
}
|
||||
|
||||
@@ -15,11 +15,9 @@
|
||||
{{ pageTitle || "Polétání.cz" }}
|
||||
</q-toolbar-title>
|
||||
|
||||
<router-link :to="{name: 'profile'}">
|
||||
<q-avatar>
|
||||
<img :src="loggedUser.avatarImageUrl" alt=""/>
|
||||
</q-avatar>
|
||||
</router-link>
|
||||
<router-link :to="{name: 'profile'}">
|
||||
<AvatarFromPhoto :size="32" :src="loggedUser.avatarImageUrl"/>
|
||||
</router-link>
|
||||
|
||||
</q-toolbar>
|
||||
</q-header>
|
||||
@@ -31,14 +29,6 @@
|
||||
>
|
||||
|
||||
<q-img class="absolute-top" :src="loggedUser.titleImageUrl" style="height: 150px">
|
||||
<!-- <div class="absolute-bottom bg-transparent">-->
|
||||
<!-- <router-link :to="{name: 'profile'}">-->
|
||||
<!-- <q-avatar size="56px" class="q-mb-sm" v-if="loggedUser.avatarImageUrl">-->
|
||||
<!-- <q-img :src="loggedUser.avatarImageUrl"/>-->
|
||||
<!-- </q-avatar>-->
|
||||
<!-- <q-avatar icon="person" color="primary" text-color="white" size="56px" v-else/>-->
|
||||
<!-- </router-link>-->
|
||||
<!-- </div>-->
|
||||
</q-img>
|
||||
<q-scroll-area style="height: calc(100% - 150px); margin-top: 150px; border-right: 1px solid #ddd">
|
||||
<q-list class="q-my-lg">
|
||||
@@ -68,6 +58,7 @@ import {storeToRefs} from "pinia";
|
||||
import BreadcrumbsNavigation from "components/BreadcrumbsNavigation.vue";
|
||||
import {useAppStore} from "stores/App";
|
||||
import {useMeta} from "quasar";
|
||||
import AvatarFromPhoto from "components/AvatarFromPhoto.vue";
|
||||
|
||||
const {pageTitle, breadcrumbs} = storeToRefs(useAppStore());
|
||||
useMeta(() => ({
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
|
||||
<template v-if="loggedUser.id">
|
||||
<router-link :to="{name: 'myFlights'}">
|
||||
<q-avatar>
|
||||
<img :src="loggedUser.avatarImageUrl" alt=""/>
|
||||
</q-avatar>
|
||||
<AvatarFromPhoto :size="35" :src="loggedUser.avatarImageUrl"/>
|
||||
</router-link>
|
||||
</template>
|
||||
<template v-else>
|
||||
@@ -39,6 +37,7 @@ import {onMounted} from "vue";
|
||||
import {useAppStore} from "stores/App";
|
||||
import {useMeta} from "quasar";
|
||||
import BreadcrumbsNavigation from "components/BreadcrumbsNavigation.vue";
|
||||
import AvatarFromPhoto from "components/AvatarFromPhoto.vue";
|
||||
|
||||
const {pageTitle, breadcrumbs} = storeToRefs(useAppStore());
|
||||
const loggedUser = storeToRefs(useUserStore()).user;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import {useUserStore} from "stores/User";
|
||||
import ProfileForm from "components/profile/ProfileForm.vue";
|
||||
import ProfileImagesForm from "components/profile/ProfileForm.vue";
|
||||
import {useAppStore} from "stores/App";
|
||||
import {storeToRefs} from "pinia";
|
||||
import {useQuasar} from "quasar";
|
||||
@@ -23,14 +23,14 @@ const openedTab = ref("profile");
|
||||
|
||||
<template>
|
||||
<q-tabs class="q-my-lg" v-model="openedTab">
|
||||
<q-tab name="profile">Profil</q-tab>
|
||||
<q-tab name="profile">Profilové fotky</q-tab>
|
||||
<q-tab name="changePassword">Změna hesla</q-tab>
|
||||
<q-tab name="publicProfile">Veřejný profil</q-tab>
|
||||
</q-tabs>
|
||||
|
||||
<q-tab-panels v-model="openedTab">
|
||||
<q-tab-panel name="profile">
|
||||
<ProfileForm :user="loggedUser" @save="handleSave"/>
|
||||
<ProfileImagesForm :user="loggedUser" @save="handleSave"/>
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="changePassword">
|
||||
|
||||
@@ -27,24 +27,29 @@ watch(flight, (newVal) => {
|
||||
|
||||
<template>
|
||||
|
||||
<FlightDetailSkeleton v-if="loading || !flight"/>
|
||||
<div>
|
||||
<FlightDetailSkeleton v-if="loading || !flight"/>
|
||||
|
||||
<template v-else>
|
||||
|
||||
<q-banner class="bg-warning text-white q-mx-md q-my-md" v-if="!flight.isPublic">
|
||||
Pozor! Tento let není publikován a nebude tak dostupný pro ostatní!
|
||||
</q-banner>
|
||||
<template v-else>
|
||||
<q-banner class="bg-warning text-white q-mx-md q-my-md" v-if="!flight.isPublic">
|
||||
Pozor! Tento let není publikován a nebude tak dostupný pro ostatní!
|
||||
</q-banner>
|
||||
|
||||
|
||||
<q-card class="q-mx-md q-my-md flight-detail-card">
|
||||
<PhotoCarousel :cover-photo-url="flight.coverPhoto.url" :photos="flight.photos"/>
|
||||
<q-card-section>
|
||||
<FlightBasicInfo :flight="flight"/>
|
||||
<q-card class="q-mx-md q-my-md flight-detail-card">
|
||||
<PhotoCarousel
|
||||
v-if="flight.coverPhoto"
|
||||
:cover-photo-url="flight.coverPhoto.url"
|
||||
:photos="flight.photos"
|
||||
/>
|
||||
<q-card-section>
|
||||
<FlightBasicInfo :flight="flight"/>
|
||||
|
||||
<article class="q-my-lg" v-html="flight.description"/>
|
||||
<article class="q-my-lg" v-html="flight.description"/>
|
||||
|
||||
<FlightTrackMap :flight="flight"/>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</template>
|
||||
<FlightTrackMap :flight="flight"/>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -8,6 +8,7 @@ import {useFlightsStore} from "stores/Flights";
|
||||
import {storeToRefs} from "pinia";
|
||||
import FlightsListSkeleton from "components/flights/FlightsListSkeleton.vue";
|
||||
import FlightsList from "components/flights/FlightsList.vue";
|
||||
import AvatarFromPhoto from "components/AvatarFromPhoto.vue";
|
||||
|
||||
const props = defineProps({
|
||||
loggedUser: {
|
||||
@@ -55,9 +56,7 @@ const flights = storeToRefs(useFlightsStore()).items;
|
||||
</div>
|
||||
<div class="col-4 sm-hide">
|
||||
<div class="text-center">
|
||||
<q-avatar size="150px">
|
||||
<img :src="pilot.avatarImageUrl" alt="">
|
||||
</q-avatar>
|
||||
<AvatarFromPhoto :size="150" :src="pilot.avatarImageUrl"/>
|
||||
</div>
|
||||
|
||||
<h3>{{ pilot.name }}</h3>
|
||||
|
||||
Reference in New Issue
Block a user