Pridani od/do k eventum, fix datetime pickeru pro vyber pouze data, pridano URL k otevrene fotce
This commit is contained in:
@@ -1,9 +1,17 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import BasicListSkeleton from "components/basicSkeletons/BasicListSkeleton.vue";
|
import BasicListSkeleton from "components/basicSkeletons/BasicListSkeleton.vue";
|
||||||
|
import BasicTextSkeleton from "components/basicSkeletons/BasicTextSkeleton.vue";
|
||||||
|
import ListItemSkeleton from "components/basicSkeletons/ListItemSkeleton.vue";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-card class="flight-detail-card">
|
|
||||||
|
<q-list bordered separator class="q-my-lg">
|
||||||
|
<ListItemSkeleton/>
|
||||||
|
<ListItemSkeleton/>
|
||||||
|
</q-list>
|
||||||
|
|
||||||
|
<BasicTextSkeleton :lines="3" :paragraphs="2"/>
|
||||||
|
|
||||||
<BasicListSkeleton :items-count-min="1" :items-count-max="5"/>
|
<BasicListSkeleton :items-count-min="1" :items-count-max="5"/>
|
||||||
</q-card>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import {commonProps} from "src/composables/inputs";
|
|||||||
import {isRequestPending} from "src/composables/form/props";
|
import {isRequestPending} from "src/composables/form/props";
|
||||||
import {isRequired} from "src/composables/form/rules";
|
import {isRequired} from "src/composables/form/rules";
|
||||||
import RichTextEditor from "components/inputs/RichTextEditor.vue";
|
import RichTextEditor from "components/inputs/RichTextEditor.vue";
|
||||||
|
import DatetimePicker from "components/inputs/DatetimePicker.vue";
|
||||||
|
import {FORMAT_DATE, FORMAT_DATETIME_LONG} from "src/composables/datetimeUtils";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
event: {
|
event: {
|
||||||
@@ -19,6 +21,8 @@ const formData = reactive({
|
|||||||
name: null,
|
name: null,
|
||||||
isPublic: false,
|
isPublic: false,
|
||||||
description: "",
|
description: "",
|
||||||
|
dateFrom: null,
|
||||||
|
dateTo: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
@@ -41,6 +45,10 @@ onMounted(() => {
|
|||||||
:rules="rules.name"
|
:rules="rules.name"
|
||||||
v-model="formData.name"
|
v-model="formData.name"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<DatetimePicker :mask="FORMAT_DATE" v-model="formData.dateFrom" label="Od" />
|
||||||
|
<DatetimePicker :mask="FORMAT_DATE" v-model="formData.dateTo" label="Do" />
|
||||||
|
|
||||||
<RichTextEditor v-model="formData.description" v-bind="commonProps" label="Popis letu, poznámky, ..."/>
|
<RichTextEditor v-model="formData.description" v-bind="commonProps" label="Popis letu, poznámky, ..."/>
|
||||||
<q-checkbox v-model="formData.isPublic" label="Veřejný?"/>
|
<q-checkbox v-model="formData.isPublic" label="Veřejný?"/>
|
||||||
|
|
||||||
|
|||||||
@@ -58,9 +58,15 @@ const props = defineProps({
|
|||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
|
||||||
<q-card v-for="i in randomInt(3, 10)" :key="i">
|
<div class="row">
|
||||||
<q-skeleton type="rect"/>
|
<div class="col-6 col-sm-4 col-md-4 col-lg-3" v-for="i in randomInt(3, 10)" :key="i">
|
||||||
|
<q-card class="q-ma-md">
|
||||||
|
<q-skeleton height="300px" type="rect"/>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div v-else-if="step === 'notes'">
|
<div v-else-if="step === 'notes'">
|
||||||
|
|||||||
@@ -26,12 +26,10 @@ const props = defineProps({
|
|||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
const dateModel = ref("");
|
const dateModel = ref("");
|
||||||
const timeModel = ref("");
|
const timeModel = ref("00:00:00");
|
||||||
const tab = ref("date");
|
const tab = ref("date");
|
||||||
|
|
||||||
let originalTime, originalDate;
|
let originalTime, originalDate;
|
||||||
|
|
||||||
|
|
||||||
const popupProxy = ref(null);
|
const popupProxy = ref(null);
|
||||||
|
|
||||||
const publicModel = computed(() => {
|
const publicModel = computed(() => {
|
||||||
@@ -42,19 +40,20 @@ const publicModel = computed(() => {
|
|||||||
return formatDatetime(dateObject, props.mask);
|
return formatDatetime(dateObject, props.mask);
|
||||||
});
|
});
|
||||||
|
|
||||||
const showTimePicker = computed(() => containsAnyOf(["H", "h", "m"], props.mask));
|
|
||||||
|
|
||||||
watch(() => dateModel.value, (newVal, oldVal) => {
|
|
||||||
tab.value = "time";
|
|
||||||
});
|
|
||||||
|
|
||||||
const model = computed(() => {
|
const model = computed(() => {
|
||||||
if (dateModel.value === '' || timeModel.value === '') return null;
|
if (dateModel.value === '') return null;
|
||||||
return dateModel.value + "T" + timeModel.value
|
let modelVal = dateModel.value;
|
||||||
|
if (showTimePicker.value) {
|
||||||
|
modelVal += "T" + timeModel.value;
|
||||||
|
} else {
|
||||||
|
modelVal += "T00:00:00"
|
||||||
|
}
|
||||||
|
|
||||||
|
return modelVal
|
||||||
});
|
});
|
||||||
|
|
||||||
const emitModelUpdate = () => {
|
const emitModelUpdate = () => {
|
||||||
if (dateModel.value === '' || timeModel.value === '') {
|
if (dateModel.value === '' || timeModel.value === '') {
|
||||||
// console.log(dateModel.value, timeModel.value);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +64,12 @@ const emitModelUpdate = () => {
|
|||||||
popupProxy.value.hide();
|
popupProxy.value.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showTimePicker = computed(() => containsAnyOf(["H", "h", "m"], props.mask));
|
||||||
|
|
||||||
|
watch(() => dateModel.value, (newVal, oldVal) => {
|
||||||
|
if (showTimePicker.value)
|
||||||
|
tab.value = "time";
|
||||||
|
});
|
||||||
const containsAnyOf = (needles, haystack) => {
|
const containsAnyOf = (needles, haystack) => {
|
||||||
for (const i in needles) {
|
for (const i in needles) {
|
||||||
if (haystack.includes(needles[i])) return true;
|
if (haystack.includes(needles[i])) return true;
|
||||||
@@ -123,7 +128,6 @@ watch(() => props.modelValue, (newVal) => {
|
|||||||
emitModelUpdate();
|
emitModelUpdate();
|
||||||
}
|
}
|
||||||
}, {immediate: true});
|
}, {immediate: true});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -134,7 +138,7 @@ watch(() => props.modelValue, (newVal) => {
|
|||||||
|
|
||||||
<template #default>
|
<template #default>
|
||||||
<q-popup-proxy ref="popupProxy" @hide="emitModelUpdate" @show="setOriginalValues">
|
<q-popup-proxy ref="popupProxy" @hide="emitModelUpdate" @show="setOriginalValues">
|
||||||
<div class="proxy-wrapper relative-position" style="min-height: 500px">
|
<div class="proxy-wrapper relative-position" :style="`min-height: ${showTimePicker ? '500' : '300'}px`">
|
||||||
|
|
||||||
<q-tabs v-model="tab" v-if="showTimePicker" class="bg-primary text-white">
|
<q-tabs v-model="tab" v-if="showTimePicker" class="bg-primary text-white">
|
||||||
<q-tab name="date" icon="event" :label="dateModel ? formatDatetime(dateModel, 'D. M.', FORMAT_DATE_ISO) : 'datum'"/>
|
<q-tab name="date" icon="event" :label="dateModel ? formatDatetime(dateModel, 'D. M.', FORMAT_DATE_ISO) : 'datum'"/>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import PhotoDetail from "components/photos/PhotoDetail.vue";
|
import PhotoDetail from "components/photos/PhotoDetail.vue";
|
||||||
import { ref, watch} from "vue";
|
import {onMounted, ref, watch} from "vue";
|
||||||
|
import {useRoute, useRouter} from "vue-router";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
photos: {
|
photos: {
|
||||||
@@ -26,13 +27,23 @@ const openPhotoDetail = ref(false);
|
|||||||
const photoData = ref(null);
|
const photoData = ref(null);
|
||||||
const openDetail = ({photo, idx, allPhotos}) => {
|
const openDetail = ({photo, idx, allPhotos}) => {
|
||||||
openPhotoDetail.value = true;
|
openPhotoDetail.value = true;
|
||||||
photoData.value = {photo, idx, allPhotos}
|
photoData.value = {photo, idx, allPhotos};
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.photos, (newVal) => {
|
watch(() => props.photos, (newVal) => {
|
||||||
if (newVal.length === 0) return;
|
if (newVal.length === 0) return;
|
||||||
displayedPhoto.value = props.coverPhotoUrl || newVal[0].url;
|
displayedPhoto.value = props.coverPhotoUrl || newVal[0].url;
|
||||||
}, {immediate: true});
|
}, {immediate: true});
|
||||||
|
|
||||||
|
const $route = useRoute();
|
||||||
|
onMounted(() => {
|
||||||
|
const photoId = parseInt($route.query.photo);
|
||||||
|
const idx = props.photos.findIndex(p => p.id === photoId);
|
||||||
|
if (idx >= 0) {
|
||||||
|
const photo = props.photos[idx];
|
||||||
|
openDetail({photo, allPhotos: props.photos, idx});
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {computed, ref, watch} from "vue";
|
|||||||
import {formatDatetime} from "src/composables/datetimeUtils";
|
import {formatDatetime} from "src/composables/datetimeUtils";
|
||||||
import 'vue-inner-image-zoom/lib/vue-inner-image-zoom.css'
|
import 'vue-inner-image-zoom/lib/vue-inner-image-zoom.css'
|
||||||
import {useQuasar} from "quasar";
|
import {useQuasar} from "quasar";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
photos: {
|
photos: {
|
||||||
@@ -27,6 +28,8 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
const $router = useRouter();
|
||||||
|
|
||||||
const imageFullscreen = ref(false);
|
const imageFullscreen = ref(false);
|
||||||
const isMaximized = computed(() => $q.screen.width <= $q.screen.sizes.sm);
|
const isMaximized = computed(() => $q.screen.width <= $q.screen.sizes.sm);
|
||||||
|
|
||||||
@@ -68,6 +71,15 @@ const openDetail = (idx) => {
|
|||||||
scrollToPhoto();
|
scrollToPhoto();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(() => displayedPhotoIndex.value, (newVal) => {
|
||||||
|
const photo = props.photos[newVal];
|
||||||
|
$router.replace({query: {photo: photo.id}});
|
||||||
|
}, {immediate: true});
|
||||||
|
|
||||||
|
const removePhotoFromQueries = () => {
|
||||||
|
$router.replace({query: {photo: undefined}});
|
||||||
|
}
|
||||||
|
|
||||||
const showExtra = ref(false);
|
const showExtra = ref(false);
|
||||||
const scrollArea = ref(null);
|
const scrollArea = ref(null);
|
||||||
const infoWrapper = ref(null);
|
const infoWrapper = ref(null);
|
||||||
@@ -92,6 +104,8 @@ const showExtraInfo = () => {
|
|||||||
v-model="isDetailOpened"
|
v-model="isDetailOpened"
|
||||||
full-width
|
full-width
|
||||||
:maximized="isMaximized"
|
:maximized="isMaximized"
|
||||||
|
no-route-dismiss
|
||||||
|
@hide="removePhotoFromQueries"
|
||||||
>
|
>
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-scroll-area ref="scrollArea" style="height: 95vh">
|
<q-scroll-area ref="scrollArea" style="height: 95vh">
|
||||||
@@ -105,6 +119,9 @@ const showExtraInfo = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
<div class="fixed-top-right q-mt-md q-mr-md z-max" v-else>
|
||||||
|
<q-btn round size="sm" icon="close" @click="isDetailOpened = false"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<q-card-section :class="{'q-pa-none': isMaximized}">
|
<q-card-section :class="{'q-pa-none': isMaximized}">
|
||||||
<div :class="{'relative-position': true, 'image-full-height': imageFullscreen}">
|
<div :class="{'relative-position': true, 'image-full-height': imageFullscreen}">
|
||||||
|
|||||||
@@ -15,5 +15,7 @@ fragment eventFullInfo on Event {
|
|||||||
name
|
name
|
||||||
description
|
description
|
||||||
isPublic
|
isPublic
|
||||||
|
dateFrom
|
||||||
|
dateTo
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import EventDetailSkeleton from "components/event/EventDetailSkeleton.vue";
|
|||||||
import {useAppStore} from "stores/App";
|
import {useAppStore} from "stores/App";
|
||||||
import FAB from "components/FAB.vue";
|
import FAB from "components/FAB.vue";
|
||||||
import FlightsList from "components/flights/FlightsList.vue";
|
import FlightsList from "components/flights/FlightsList.vue";
|
||||||
|
import ListItemSkeleton from "components/basicSkeletons/ListItemSkeleton.vue";
|
||||||
|
import {FORMAT_DATE, FORMAT_DATETIME_LONG, formatDatetime} from "../../../composables/datetimeUtils";
|
||||||
|
|
||||||
const eventId = parseInt(useRoute().params.id);
|
const eventId = parseInt(useRoute().params.id);
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -23,7 +25,22 @@ onMounted(() => {
|
|||||||
<EventDetailSkeleton v-if="loading || !event"/>
|
<EventDetailSkeleton v-if="loading || !event"/>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<article v-html="event.description" />
|
|
||||||
|
<q-list bordered separator class="q-my-lg" v-if="event.dateFrom && event.dateTo">
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
Od: {{ formatDatetime(event.dateFrom, FORMAT_DATE) }}
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
Do: {{ formatDatetime(event.dateTo, FORMAT_DATE) }}
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
|
||||||
|
<article v-html="event.description"/>
|
||||||
|
|
||||||
<h3>Lety</h3>
|
<h3>Lety</h3>
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import FlightFormNotes from "components/flights/form/FlightFormNotes.vue";
|
|||||||
import FlightFormPhotos from "components/flights/form/FlightFormPhotos.vue";
|
import FlightFormPhotos from "components/flights/form/FlightFormPhotos.vue";
|
||||||
import {usePhotosStore} from "stores/Photo";
|
import {usePhotosStore} from "stores/Photo";
|
||||||
import PhotosList from "components/photos/PhotosList.vue";
|
import PhotosList from "components/photos/PhotosList.vue";
|
||||||
import {successObject, warningObject} from "src/composables/dialog";
|
import {errorObject, successObject, warningObject} from "src/composables/dialog";
|
||||||
import PhotoFormDialog from "components/photos/PhotoFormDialog.vue";
|
import PhotoFormDialog from "components/photos/PhotoFormDialog.vue";
|
||||||
import FlightFormTrack from "components/flights/form/FlightFormTrack.vue";
|
import FlightFormTrack from "components/flights/form/FlightFormTrack.vue";
|
||||||
import {fetchFlight, flight, loading} from "src/composables/objects/flight";
|
import {fetchFlight, flight, loading} from "src/composables/objects/flight";
|
||||||
@@ -118,8 +118,15 @@ const rotatePhotos = ({photos, angle}) => {
|
|||||||
flight.value.photos[idx] = value;
|
flight.value.photos[idx] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success > 0)
|
const error = result.length - success;
|
||||||
$q.notify(successObject("Otočeno " + success + " fotek"));
|
|
||||||
|
if (success > 0) {
|
||||||
|
$q.notify(successObject("Otočeno " + success + " fotografií"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error > 0) {
|
||||||
|
$q.notify(errorObject(`Nepovedlo se otočit ${error} fotografi`))
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -190,7 +197,5 @@ const rotatePhotos = ({photos, angle}) => {
|
|||||||
<FlightFormNotes @save="handleFlightEdit" :events="events" :flight="flight"/>
|
<FlightFormNotes @save="handleFlightEdit" :events="events" :flight="flight"/>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
</q-tab-panels>
|
</q-tab-panels>
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user