mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
add feature add and delete range
This commit is contained in:
parent
75b2f6419e
commit
3ea630748a
@ -1,5 +1,5 @@
|
|||||||
import {EventInput} from '@fullcalendar/vue3';
|
import {EventInput} from '@fullcalendar/vue3';
|
||||||
import {DateTime, User} from 'ChillMainAssets/types';
|
import {DateTime, User, UserAssociatedInterface} from 'ChillMainAssets/types';
|
||||||
|
|
||||||
export interface CalendarRange {
|
export interface CalendarRange {
|
||||||
id: number;
|
id: number;
|
||||||
@ -12,6 +12,12 @@ export interface CalendarRange {
|
|||||||
updatedBy: User;
|
updatedBy: User;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CalendarRangeEdit {
|
||||||
|
user: UserAssociatedInterface,
|
||||||
|
startDate: DateTime,
|
||||||
|
endDate: DateTime
|
||||||
|
}
|
||||||
|
|
||||||
export interface Calendar {
|
export interface Calendar {
|
||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
@ -23,4 +29,11 @@ export interface CalendarRemote {
|
|||||||
title: string
|
title: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type EventInputCalendarRange = EventInput & {
|
||||||
|
id: string,
|
||||||
|
userId: number,
|
||||||
|
calendarRangeId: number,
|
||||||
|
is: "range"
|
||||||
|
};
|
||||||
|
|
||||||
export {};
|
export {};
|
||||||
|
@ -2,6 +2,7 @@ import {COLORS} from '../const';
|
|||||||
import {ISOToDatetime} from 'ChillMainAssets/chill/js/date';
|
import {ISOToDatetime} from 'ChillMainAssets/chill/js/date';
|
||||||
import {DateTime, User} from 'ChillMainAssets/types';
|
import {DateTime, User} from 'ChillMainAssets/types';
|
||||||
import {CalendarRange, CalendarRemote} from 'ChillCalendarAssets/types';
|
import {CalendarRange, CalendarRemote} from 'ChillCalendarAssets/types';
|
||||||
|
import type {EventInputCalendarRange} from 'ChillCalendarAssets/types';
|
||||||
import {EventInput} from '@fullcalendar/vue3';
|
import {EventInput} from '@fullcalendar/vue3';
|
||||||
|
|
||||||
export interface UserData {
|
export interface UserData {
|
||||||
@ -63,7 +64,7 @@ export const createUserData = (user: User, colorIndex: number): UserData => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO move this function to a more global namespace, as it is in use in MyCalendarRange app
|
// TODO move this function to a more global namespace, as it is in use in MyCalendarRange app
|
||||||
export const calendarRangeToFullCalendarEvent = (entity: CalendarRange): EventInput & {id: string} => {
|
export const calendarRangeToFullCalendarEvent = (entity: CalendarRange): EventInputCalendarRange => {
|
||||||
return {
|
return {
|
||||||
id: `range_${entity.id}`,
|
id: `range_${entity.id}`,
|
||||||
title: "(" + entity.user.text + ")",
|
title: "(" + entity.user.text + ")",
|
||||||
|
@ -48,7 +48,7 @@ import frLocale from '@fullcalendar/core/locales/fr';
|
|||||||
import FullCalendar from '@fullcalendar/vue3';
|
import FullCalendar from '@fullcalendar/vue3';
|
||||||
import interactionPlugin from "@fullcalendar/interaction";
|
import interactionPlugin from "@fullcalendar/interaction";
|
||||||
import timeGridPlugin from "@fullcalendar/timegrid";
|
import timeGridPlugin from "@fullcalendar/timegrid";
|
||||||
import {EventApi} from "@fullcalendar/core";
|
import {EventApi, DateSelectArg} from "@fullcalendar/core";
|
||||||
|
|
||||||
const store = useStore(key);
|
const store = useStore(key);
|
||||||
|
|
||||||
@ -64,7 +64,10 @@ const baseOptions = ref<CalendarOptions>({
|
|||||||
initialView: 'timeGridWeek',
|
initialView: 'timeGridWeek',
|
||||||
initialDate: new Date(),
|
initialDate: new Date(),
|
||||||
selectable: true,
|
selectable: true,
|
||||||
|
// when the dates are changes in the fullcalendar view OR when new events are added
|
||||||
datesSet: onDatesSet,
|
datesSet: onDatesSet,
|
||||||
|
// when a date is selected
|
||||||
|
select: onDateSelect,
|
||||||
selectMirror: false,
|
selectMirror: false,
|
||||||
editable: true,
|
editable: true,
|
||||||
slotMinTime: "08:00:00",
|
slotMinTime: "08:00:00",
|
||||||
@ -121,15 +124,25 @@ const calendarOptions = computed((): CalendarOptions => {
|
|||||||
/**
|
/**
|
||||||
* launched when the calendar range date change
|
* launched when the calendar range date change
|
||||||
*/
|
*/
|
||||||
function onDatesSet(event: DatesSetArg) {
|
function onDatesSet(event: DatesSetArg): void {
|
||||||
store.dispatch('fullCalendar/setCurrentDatesView', {start: event.start, end: event.end});
|
store.dispatch('fullCalendar/setCurrentDatesView', {start: event.start, end: event.end});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onDateSelect(event: DateSelectArg): void {
|
||||||
|
store.dispatch('calendarRanges/createRange', {start: event.start, end: event.end});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When a calendar range is deleted
|
* When a calendar range is deleted
|
||||||
*/
|
*/
|
||||||
function onClickDelete(event: EventApi) {
|
function onClickDelete(event: EventApi): void {
|
||||||
console.log('onClickDelete', event);
|
console.log('onClickDelete', event);
|
||||||
|
|
||||||
|
if (event.extendedProps.is !== 'range') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch('calendarRanges/deleteRange', event.extendedProps.calendarRangeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ export interface State {
|
|||||||
calendarRanges: CalendarRangesState,
|
calendarRanges: CalendarRangesState,
|
||||||
calendarRemotes: CalendarRemotesState,
|
calendarRemotes: CalendarRemotesState,
|
||||||
fullCalendar: FullCalendarState,
|
fullCalendar: FullCalendarState,
|
||||||
|
me: MeState,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const key: InjectionKey<Store<State>> = Symbol();
|
export const key: InjectionKey<Store<State>> = Symbol();
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
import {State} from './../index';
|
import {State} from './../index';
|
||||||
import {ActionContext, Module} from 'vuex';
|
import {ActionContext, Module} from 'vuex';
|
||||||
import {CalendarRange/*, CalendarRangeEvent*/} from 'ChillCalendarAssets/types';
|
import {CalendarRange, CalendarRangeEdit} from "ChillCalendarAssets/types";
|
||||||
import {fetchCalendarRangeForUser} from 'ChillCalendarAssets/vuejs/Calendar/api';
|
import {fetchCalendarRangeForUser} from 'ChillCalendarAssets/vuejs/Calendar/api';
|
||||||
import {calendarRangeToFullCalendarEvent/*, CalendarRangeEvent*/} from 'ChillCalendarAssets/vuejs/Calendar/store/utils';
|
import {calendarRangeToFullCalendarEvent} from 'ChillCalendarAssets/vuejs/Calendar/store/utils';
|
||||||
import {EventInput, EventSource} from '@fullcalendar/vue3';
|
import {UserAssociatedInterface} from "../../../../../../../ChillMainBundle/Resources/public/types";
|
||||||
|
import {EventInput} from '@fullcalendar/vue3';
|
||||||
|
import {makeFetch} from "../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
||||||
|
import {datetimeToISO} from "../../../../../../../ChillMainBundle/Resources/public/chill/js/date";
|
||||||
|
import type {EventInputCalendarRange} from "ChillCalendarAssets/types";
|
||||||
|
|
||||||
export interface CalendarRangesState {
|
export interface CalendarRangesState {
|
||||||
ranges: EventInput[],
|
ranges: (EventInput | EventInputCalendarRange) [],
|
||||||
rangesLoaded: { start: number, end: number }[],
|
rangesLoaded: { start: number, end: number }[],
|
||||||
rangesIndex: Set<string>,
|
rangesIndex: Set<string>,
|
||||||
key: number
|
key: number
|
||||||
@ -35,12 +39,12 @@ export default <Module<CalendarRangesState, State>> {
|
|||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
addRanges(state: CalendarRangesState, ranges: CalendarRange[]) {
|
addRanges(state: CalendarRangesState, ranges: CalendarRange[]) {
|
||||||
console.log('addRanges', ranges);
|
|
||||||
|
|
||||||
const toAdd = ranges
|
const toAdd = ranges
|
||||||
.map(cr => calendarRangeToFullCalendarEvent(cr))
|
.map(cr => calendarRangeToFullCalendarEvent(cr))
|
||||||
.map(cr => ({...cr, backgroundColor: 'white', borderColor:'#3788d8',
|
.map(cr => ({
|
||||||
textColor: 'black'}))
|
...cr, backgroundColor: 'white', borderColor: '#3788d8',
|
||||||
|
textColor: 'black'
|
||||||
|
}))
|
||||||
.filter(r => !state.rangesIndex.has(r.id));
|
.filter(r => !state.rangesIndex.has(r.id));
|
||||||
|
|
||||||
toAdd.forEach((r) => {
|
toAdd.forEach((r) => {
|
||||||
@ -72,17 +76,20 @@ export default <Module<CalendarRangesState, State>> {
|
|||||||
state.rangesIndex.add(asEvent.id);
|
state.rangesIndex.add(asEvent.id);
|
||||||
state.key = state.key + 1;
|
state.key = state.key + 1;
|
||||||
},
|
},
|
||||||
removeRange(state: CalendarRangesState, payload: EventInput) {
|
removeRange(state: CalendarRangesState, calendarRangeId: number) {
|
||||||
/*
|
const found = state.ranges.find(r => r.calendarRangeId === calendarRangeId && r.is === "range");
|
||||||
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;
|
|
||||||
|
|
||||||
*/
|
if (found !== undefined) {
|
||||||
|
state.ranges = state.ranges.filter(
|
||||||
|
(r) => !(r.calendarRangeId === calendarRangeId && r.is === "range")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (typeof found.id === "string") { // should always be true
|
||||||
|
state.rangesIndex.delete(found.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.key = state.key + 1;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
@ -115,6 +122,48 @@ export default <Module<CalendarRangesState, State>> {
|
|||||||
ctx.commit('addRanges', ranges);
|
ctx.commit('addRanges', ranges);
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
createRange(ctx, {start, end}: { start: Date, end: Date }): Promise<null> {
|
||||||
|
const url = `/api/1.0/calendar/calendar-range.json?`;
|
||||||
|
|
||||||
|
if (ctx.rootState.me.me === null) {
|
||||||
|
throw new Error('user is currently null');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
user: {
|
||||||
|
id: ctx.rootState.me.me.id,
|
||||||
|
type: "user"
|
||||||
|
},
|
||||||
|
startDate: {
|
||||||
|
datetime: datetimeToISO(start),
|
||||||
|
},
|
||||||
|
endDate: {
|
||||||
|
datetime: datetimeToISO(end)
|
||||||
|
},
|
||||||
|
} as CalendarRangeEdit;
|
||||||
|
|
||||||
|
return makeFetch<CalendarRangeEdit, CalendarRange>('POST', url, body)
|
||||||
|
.then((newRange) => {
|
||||||
|
|
||||||
|
ctx.commit('addRange', newRange);
|
||||||
|
|
||||||
|
return Promise.resolve(null);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deleteRange({commit}, calendarRangeId: number) {
|
||||||
|
const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
|
||||||
|
|
||||||
|
makeFetch<undefined, never>('DELETE', url)
|
||||||
|
.then((_) => {
|
||||||
|
commit('removeRange', calendarRangeId);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -51,13 +51,15 @@ export interface NotFoundExceptionInterface extends TransportExceptionInterface
|
|||||||
export interface ServerExceptionInterface extends TransportExceptionInterface {
|
export interface ServerExceptionInterface extends TransportExceptionInterface {
|
||||||
name: 'ServerException';
|
name: 'ServerException';
|
||||||
message: string;
|
message: string;
|
||||||
|
code: number;
|
||||||
|
body: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic api method that can be adapted to any fetch request
|
* Generic api method that can be adapted to any fetch request
|
||||||
*/
|
*/
|
||||||
export const makeFetch = <T, Q>(method: 'POST'|'GET'|'PUT'|'PATCH'|'DELETE', url: string, body?: body | T | null, options?: FetchParams): Promise<Q> => {
|
export const makeFetch = <Input, Output>(method: 'POST'|'GET'|'PUT'|'PATCH'|'DELETE', url: string, body?: body | Input | null, options?: FetchParams): Promise<Output> => {
|
||||||
let opts = {
|
let opts = {
|
||||||
method: method,
|
method: method,
|
||||||
headers: {
|
headers: {
|
||||||
@ -128,15 +130,31 @@ function _fetchAction<T>(page: number, uri: string, params?: FetchParams): Promi
|
|||||||
},
|
},
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
if (response.ok) { return response.json(); }
|
if (response.ok) { return response.json(); }
|
||||||
|
|
||||||
if (response.status === 404) {
|
if (response.status === 404) {
|
||||||
throw NotFoundException(response);
|
throw NotFoundException(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(response);
|
if (response.status === 422) {
|
||||||
throw ServerException();
|
return response.json().then(response => {
|
||||||
|
throw ValidationException(response)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status === 403) {
|
||||||
|
throw AccessException(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status >= 500) {
|
||||||
|
return response.text().then(body => {
|
||||||
|
throw ServerException(response.status, body);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("other network error");
|
||||||
}).catch((reason: any) => {
|
}).catch((reason: any) => {
|
||||||
console.error(reason);
|
console.error(reason);
|
||||||
throw ServerException();
|
throw new Error(reason);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -192,9 +210,11 @@ const NotFoundException = (response: Response): NotFoundExceptionInterface => {
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ServerException = (): ServerExceptionInterface => {
|
const ServerException = (code: number, body: string): ServerExceptionInterface => {
|
||||||
const error = {} as ServerExceptionInterface;
|
const error = {} as ServerExceptionInterface;
|
||||||
error.name = 'ServerException';
|
error.name = 'ServerException';
|
||||||
|
error.code = code;
|
||||||
|
error.body = body;
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -34,3 +34,8 @@ export interface User {
|
|||||||
user_job: Job;
|
user_job: Job;
|
||||||
// todo: mainCenter; mainJob; etc..
|
// todo: mainCenter; mainJob; etc..
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UserAssociatedInterface {
|
||||||
|
type: "user";
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user