edit location on existing ranges

This commit is contained in:
Julien Fastré 2022-06-29 23:47:12 +02:00
parent adad4313a6
commit 9e93e2a3f9
7 changed files with 144 additions and 49 deletions

View File

@ -64,7 +64,7 @@
<b v-if="arg.event.extendedProps.is === 'remote'">{{ arg.event.title}}</b>
<b v-else-if="arg.event.extendedProps.is === 'range'">{{ arg.timeText }} - {{ arg.event.extendedProps.locationName }}</b>
<b v-else >no 'is'</b>
<a v-if="arg.event.extendedProps.is === 'range'" class="fa fa-fw fa-times"
<a v-if="arg.event.extendedProps.is === 'range'" class="fa fa-fw fa-times delete"
@click.prevent="onClickDelete(arg.event)">
</a>
</span>
@ -72,7 +72,7 @@
</FullCalendar>
<div id="copy-widget">
<h4 class="chill-red" style="margin-top: 2rem;">{{ 'copy_range_from_to' }}</h4>
<h4 class="chill-red" style="margin-top: 2rem;">{{ $t('copy_range_from_to') }}</h4>
<div style="display: flex; margin-top: 1rem;">
<div class="col-sm-3" style="margin-right: 1rem">
<input class="form-control" type="date" v-model="copyFrom" />
@ -82,10 +82,13 @@
<input class="form-control" type="date" v-model="copyTo" />
</div>
<button class="btn btn-action" @click="copyDay">
{{ 'copy_range' }}
{{ $t('copy_range') }}
</button>
</div>
</div>
<!-- not directly seen, but include in a modal -->
<edit-location ref="editLocation"></edit-location>
</template>
<script setup lang="ts">
@ -104,10 +107,11 @@ import FullCalendar from '@fullcalendar/vue3';
import frLocale from '@fullcalendar/core/locales/fr';
import interactionPlugin, {DropArg, EventResizeDoneArg} from "@fullcalendar/interaction";
import timeGridPlugin from "@fullcalendar/timegrid";
import {EventApi, DateSelectArg, EventDropArg} from "@fullcalendar/core";
import {EventApi, DateSelectArg, EventDropArg, EventClickArg} from "@fullcalendar/core";
import {ISOToDate, ISOToDatetime} from "../../../../../ChillMainBundle/Resources/public/chill/js/date";
import VueMultiselect from "vue-multiselect";
import {Location} from "../../../../../ChillMainBundle/Resources/public/types";
import EditLocation from "./Components/EditLocation.vue";
const store = useStore(key);
@ -117,6 +121,7 @@ const slotMinTime = ref('08:00:00');
const slotMaxTime = ref('19:00:00');
const copyFrom = ref<string | null>(null);
const copyTo = ref<string | null>(null);
const editLocation = ref<InstanceType<typeof EditLocation> | null>(null)
const baseOptions = ref<CalendarOptions>({
locale: frLocale,
@ -133,6 +138,8 @@ const baseOptions = ref<CalendarOptions>({
eventResize: onEventDropOrResize,
// when an event is moved
eventDrop: onEventDropOrResize,
// when an event si clicked
eventClick: onEventClick,
selectMirror: false,
editable: true,
headerToolbar: {
@ -155,7 +162,7 @@ const pickedLocation = computed<Location | null>({
return store.state.locations.locationPicked || store.state.locations.currentLocation;
},
set(newLocation: Location | null): void {
store.commit('locations/setLocationPicked', newLocation, { root: true});
store.commit('locations/setLocationPicked', newLocation, {root: true});
}
})
@ -229,8 +236,10 @@ function onClickDelete(event: EventApi): void {
}
function onEventDropOrResize(payload: EventDropArg | EventResizeDoneArg) {
if (payload.event.extendedProps.is !== 'range') {
return;
}
const changedEvent = payload.event;
console.log('eventDropOrResize', payload);
store.dispatch('calendarRanges/patchRangeTime', {
calendarRangeId: payload.event.extendedProps.calendarRangeId,
@ -239,6 +248,18 @@ function onEventDropOrResize(payload: EventDropArg | EventResizeDoneArg) {
});
};
function onEventClick(payload: EventClickArg): void {
// @ts-ignore TS does not recognize the target. But it does exists.
if (payload.jsEvent.target.classList.contains('delete')) {
return;
}
if (payload.event.extendedProps.is !== 'range') {
return;
}
editLocation.value?.startEdit(payload.event);
}
function copyDay() {
if (null === copyFrom.value || null === copyTo.value) {
return;

View File

@ -0,0 +1,71 @@
<template>
<teleport to="body">
<modal v-if="showModal"
@close="showModal = false">
<template v-slot:header>
<div>boum!</div>
</template>
<template v-slot:body>
<label>Localisation</label>
<vue-multiselect v-model="pickedLocation" :options="locations" :label="'name'" :track-by="'id'"></vue-multiselect>
</template>
<template v-slot:footer>
<ul class="record_actions">
<li>close</li>
</ul>
</template>
</modal>
</teleport>
</template>
<script setup lang="ts">
import Modal from "../../../../../../ChillMainBundle/Resources/public/vuejs/_components/Modal.vue";
import {computed, ref} from "vue";
import {EventApi} from "@fullcalendar/vue3";
import {useStore} from "vuex";
import {key} from "../store";
import {Location} from "../../../../../../ChillMainBundle/Resources/public/types";
import VueMultiselect from "vue-multiselect";
const store = useStore(key);
const calendarRangeId = ref<number | null>(null);
const location = ref<Location | null>(null);
const showModal = ref(false);
const locations = computed<Location[]>(() => {
return store.state.locations.locations;
});
const pickedLocation = computed<Location | null>({
get(): Location | null {
return location.value;
},
set(newLocation: Location | null): void {
console.log('newLocation', newLocation);
store.dispatch('calendarRanges/patchRangeLocation', {location: newLocation, calendarRangeId: calendarRangeId.value})
.then(_ => {showModal.value = false;})
}
})
const startEdit = function(event: EventApi): void {
console.log('startEditing', event);
calendarRangeId.value = event.extendedProps.calendarRangeId;
location.value = store.getters['locations/getLocationById'](event.extendedProps.locationId) || null;
console.log('new location value', location.value);
console.log('calendar range id', calendarRangeId.value);
showModal.value = true;
}
defineExpose({startEdit});
</script>
<style scoped>
</style>

View File

@ -1,18 +1,18 @@
import { createApp } from 'vue';
//import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n'
//import { appMessages } from './i18n'
import { _createI18n } from '../../../../../ChillMainBundle/Resources/public/vuejs/_js/i18n'
import { appMessages } from './i18n'
import futureStore, {key} from './store/index'
import App2 from './App2.vue';
futureStore().then((store) => {
//const i18n = _createI18n(appMessages);
const i18n = _createI18n(appMessages);
const app = createApp({
template: `<app></app>`,
})
.use(store, key)
//.use(i18n)
.use(i18n)
.component('app', App2)
.mount('#myCalendar');
});

View File

@ -53,7 +53,7 @@ export default <Module<CalendarRangesState, State>>{
}
return founds;
}
},
},
mutations: {
addRanges(state: CalendarRangesState, ranges: CalendarRange[]) {
@ -83,11 +83,7 @@ export default <Module<CalendarRangesState, State>>{
},
addLoaded(state: CalendarRangesState, payload: { start: Date, end: Date }) {
state.rangesLoaded.push({start: payload.start.getTime(), end: payload.end.getTime()});
},/*
setRangesToCopy(state: State, payload: CalendarRange[]) {
state.rangesToCopy = payload
},*/
},
addRange(state: CalendarRangesState, payload: CalendarRange) {
const asEvent = calendarRangeToFullCalendarEvent(payload);
state.ranges.push({...asEvent, backgroundColor: 'white', borderColor: '#3788d8', textColor: 'black'});
@ -116,6 +112,8 @@ export default <Module<CalendarRangesState, State>>{
if (found !== undefined) {
found.start = newEvent.start;
found.end = newEvent.end;
found.locationId = range.location.id;
found.locationName = range.location.name;
}
state.key = state.key + 1;
@ -194,7 +192,7 @@ export default <Module<CalendarRangesState, State>>{
ctx.commit('removeRange', calendarRangeId);
});
},
patchRangeTime(ctx, {calendarRangeId, start, end}: {calendarRangeId: number, start: Date, end: Date}): void {
patchRangeTime(ctx, {calendarRangeId, start, end}: {calendarRangeId: number, start: Date, end: Date}): Promise<null> {
const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
const body = {
startDate: {
@ -205,12 +203,33 @@ export default <Module<CalendarRangesState, State>>{
},
} as CalendarRangeEdit;
makeFetch<CalendarRangeEdit, CalendarRange>('PATCH', url, body)
return makeFetch<CalendarRangeEdit, CalendarRange>('PATCH', url, body)
.then((range) => {
ctx.commit('updateRange', range);
return Promise.resolve(null);
})
.catch((error) => {
console.error(error);
return Promise.resolve(null);
})
},
patchRangeLocation(ctx, {location, calendarRangeId}: {location: Location, calendarRangeId: number}): Promise<null> {
const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
const body = {
location: {
id: location.id,
type: "location"
},
} as CalendarRangeEdit;
return makeFetch<CalendarRangeEdit, CalendarRange>('PATCH', url, body)
.then((range) => {
ctx.commit('updateRange', range);
return Promise.resolve(null);
})
.catch((error) => {
console.error(error);
return Promise.resolve(null);
})
},
copyFromDayToAnotherDay(ctx, {from, to}: {from: Date, to: Date}): Promise<null> {

View File

@ -51,43 +51,18 @@ export default <Module<CalendarRemotesState, State>> {
},
addLoaded(state: CalendarRemotesState, payload: {start: Date, end: Date}) {
state.remotesLoaded.push({start: payload.start.getTime(), end: payload.end.getTime()});
},/*
setRangesToCopy(state: State, payload: CalendarRange[]) {
state.rangesToCopy = payload
},*/
addRemote(state: CalendarRemotesState, payload: CalendarRemote) {
const asEvent = remoteToFullCalendarEvent(payload);
state.remotes.push(asEvent);
state.remotesIndex.add(asEvent.id);
state.key = state.key + 1;
},
removeRemote(state: CalendarRemotesState, payload: EventInput) {
/*
state.ranges = state.ranges.filter(
(r) => r.id !== payload.id
);
if (typeof payload.id === "string") {
state.rangesIndex.delete(payload.id);
}
state.key = state.key + 1;
*/
},
},
actions: {
fetchRemotes(ctx: Context, payload: {start: Date, end: Date}): Promise<null> {
console.log('fetchRanges', payload);
const start = payload.start;
const end = payload.end;
if (ctx.rootGetters['me/getMe'] === null) {
console.log('me is not there');
return Promise.resolve(null);
}
if (ctx.getters.isRemotesLoaded({start, end})) {
console.log('range already loaded');
return Promise.resolve(ctx.getters.getRangeSource);
}
@ -102,6 +77,7 @@ export default <Module<CalendarRemotesState, State>> {
end
)
.then((remotes: CalendarRemote[]) => {
// to be add when reactivity problem will be solve ?
//ctx.commit('addRemotes', remotes);
const inputs = remotes
.map(cr => remoteToFullCalendarEvent(cr))

View File

@ -19,6 +19,11 @@ export default <Module<LocationState, State>>{
currentLocation: null,
}
},
getters: {
getLocationById: (state) => (id: number): Location|undefined => {
return state.locations.find(l => l.id === id);
},
},
mutations: {
setLocations(state, locations): void {
state.locations = locations;

View File

@ -26,7 +26,8 @@
</transition>
</template>
<script>
<script lang="ts">
import {defineComponent} from "vue";
/*
* This Modal component is a mix between Vue3 modal implementation
* [+] with 'v-if:showModal' directive:parameter, html scope is added/removed not just shown/hidden
@ -36,20 +37,22 @@
* [+] using bootstrap css classes, the modal have a responsive behaviour,
* [+] modal design can be configured using css classes (size, scroll)
*/
export default {
export default defineComponent({
name: 'Modal',
props: {
modalDialogClass: {
type: String,
required: false
type: Object,
required: false,
default: {},
},
hideFooter: {
type: Boolean,
required: false
required: false,
default: false
}
},
emits: ['close']
}
});
</script>
<style lang="scss">