diff --git a/app/controllers/parameters/planning_parameters_controller.rb b/app/controllers/parameters/planning_parameters_controller.rb index 3a57a8ce..ec09ed8d 100644 --- a/app/controllers/parameters/planning_parameters_controller.rb +++ b/app/controllers/parameters/planning_parameters_controller.rb @@ -79,11 +79,18 @@ def get_hours_before_cancelling_activity end end - def show_activity_code - render json: { show_activity_code: Parameter.get_value("planning.card.show_activity_code", default: false) } + def school_planning_params + authorize! :manage, Parameter + + render json: { + show_activity_code: Parameter.get_value("planning.card.show_activity_code", default: false), + recurrence_activated: Parameter.get_value("planning.recurrence_activated", default: false) + } end - def update_show_activity_code + def update_school_planning_params + authorize! :manage, Parameter + show_activity_code = Parameter.find_or_create_by( label: "planning.card.show_activity_code", value_type: "boolean" @@ -91,10 +98,19 @@ def update_show_activity_code show_activity_code.value = (params[:show_activity_code]&.to_s == "true").to_s - saved = show_activity_code.save! + show_activity_code.save! + + recurrence_activated = Parameter.find_or_create_by( + label: "planning.recurrence_activated", + value_type: "boolean" + ) + + recurrence_activated.value = (params[:recurrence_activated]&.to_s == "true").to_s + + recurrence_activated.save! respond_to do |format| - format.json { render json: { success: saved } } + format.json { render json: { success: true } } end end end diff --git a/app/controllers/planning_controller.rb b/app/controllers/planning_controller.rb index 0be3ae70..934968c3 100755 --- a/app/controllers/planning_controller.rb +++ b/app/controllers/planning_controller.rb @@ -1,6 +1,8 @@ class PlanningController < ApplicationController skip_before_action :verify_authenticity_token, only: [:update_availabilities] + before_action -> { @recurrence_activated = Parameter.get_value("planning.recurrence_activated", default: false) }, only: %i[show show_generic show_all_rooms show_for_conflict show_for_room] + def index_for_teachers @current_user = current_user plannings = Planning.includes(:user).where(users: { is_teacher: true }) diff --git a/app/models/planning.rb b/app/models/planning.rb index ff35fbca..013c857c 100755 --- a/app/models/planning.rb +++ b/app/models/planning.rb @@ -38,19 +38,54 @@ def update_intervals(intervals, season_id) end intervals.each do |i| + # isRecurrent if only a param on planning when new interval is created + if i["recurrentType"]&.present? + interval = TimeInterval.new(start: i["start"], end: i["end"], kind: i["kind"] || "d", is_validated: i["is_validated"] || false) + + season = Season.from_interval(interval).first || Season.current + + recurrence_end = season.end + recurrence_step = case i["recurrentType"] + when "weekly" + 1.week + when "biweekly" + 2.weeks + when "monthly" + 1.month + when "bimonthly" + 2.months + when "yearly" + 1.year + else + 1.week + end + + holiday_dates = season.holidays.map { |h| h.date } + + while interval.end < recurrence_end + next if holiday_dates.include?(interval.start&.to_date) + intervalList << interval if interval.save + + interval = interval.dup + interval.id = nil + interval.start += recurrence_step + interval.end += recurrence_step + end + else if i["isNew"] == true - interval = TimeInterval.new + interval = TimeInterval.new else - interval = TimeInterval.find_or_initialize_by(id: i['id']) + interval = TimeInterval.find_or_initialize_by(id: i['id']) end interval.start = i["start"] interval.end = i["end"] if !season.nil? - interval.convert_to_first_week_of_season(season) + interval.convert_to_first_week_of_season(season) end interval.kind = i["kind"] || "d" interval.is_validated = i["is_validated"] || false interval.save + end if self.time_intervals.where(id: interval.id).none? intervalList << interval diff --git a/app/views/planning/show.html.erb b/app/views/planning/show.html.erb index fc159b8f..86541a14 100755 --- a/app/views/planning/show.html.erb +++ b/app/views/planning/show.html.erb @@ -74,5 +74,6 @@ show_availabilities: @show_availabilities, teacher_can_edit: @teacher_can_edit, currentUserId: current_user&.id, - show_activity_code: @show_activity_code + show_activity_code: @show_activity_code, + recurrenceActivated: @recurrence_activated }) %> diff --git a/app/views/planning/show_all_rooms.html.erb b/app/views/planning/show_all_rooms.html.erb index 94e771a1..ca016153 100644 --- a/app/views/planning/show_all_rooms.html.erb +++ b/app/views/planning/show_all_rooms.html.erb @@ -2,7 +2,7 @@

<%= @rooms.select {|r| r.id == room_id.to_i}.first.label %>

<%= react_component("planning/Planning", { show_activity_code: @show_activity_code, room: @rooms.select {|r| r.id == room_id}.first, isTeacher: current_user.teacher?, isAdmin: current_user.admin?, displayOnly: true, intervals: time_intervals, rooms: @rooms, modal: false, levels: @levels, locations: @locations, room_refs: @rooms, isRoom: true, planning: time_intervals, activity_refs: @activity_refs, teachers: @teachers, - currentUserId: current_user&.id}) %> + currentUserId: current_user&.id, recurrenceActivated: @recurrence_activated}) %>
<% end %> diff --git a/app/views/planning/show_for_conflict.html.erb b/app/views/planning/show_for_conflict.html.erb index 06713692..3a548892 100644 --- a/app/views/planning/show_for_conflict.html.erb +++ b/app/views/planning/show_for_conflict.html.erb @@ -16,5 +16,6 @@ season: @season, conflict: @conflict, currentUserId: current_user&.id, - show_activity_code: @show_activity_code + show_activity_code: @show_activity_code, + recurrenceActivated: @recurrence_activated }) %> diff --git a/app/views/planning/show_for_room.html.erb b/app/views/planning/show_for_room.html.erb index 954ae8f7..897dbe10 100755 --- a/app/views/planning/show_for_room.html.erb +++ b/app/views/planning/show_for_room.html.erb @@ -37,5 +37,6 @@ teachers: @teachers, displayRaw: true, currentUserId: current_user&.id, - show_activity_code: @show_activity_code + show_activity_code: @show_activity_code, + recurrenceActivated: @recurrence_activated }) %> diff --git a/app/views/planning/show_generic.html.erb b/app/views/planning/show_generic.html.erb index b60d1321..612eea47 100644 --- a/app/views/planning/show_generic.html.erb +++ b/app/views/planning/show_generic.html.erb @@ -70,6 +70,7 @@ day: @day, generic: true, currentUserId: current_user&.id, - show_activity_code: @show_activity_code + show_activity_code: @show_activity_code, + recurrenceActivated: @recurrence_activated }) %> diff --git a/config/routes.rb b/config/routes.rb index 00d23cd8..188bbc62 100755 --- a/config/routes.rb +++ b/config/routes.rb @@ -53,8 +53,8 @@ get "planning_parameters", to: "planning_parameters#index" post "planning_parameters", to: "planning_parameters#update" - get "show_activity_code", to: "planning_parameters#show_activity_code" - post "show_activity_code", to: "planning_parameters#update_show_activity_code" + get "planning/school_planning_params", to: "planning_parameters#school_planning_params" + post "planning/school_planning_params", to: "planning_parameters#update_school_planning_params" get "hours_before_cancelling_activity", to: "planning_parameters#get_hours_before_cancelling_activity" post "hours_before_cancelling_activity", to: "planning_parameters#save_hours_before_cancelling_activity" diff --git a/frontend/components/parameters/Plannings/PlanningDisplayParameters.jsx b/frontend/components/parameters/Plannings/PlanningDisplayParameters.jsx index c48d9177..618ad9ab 100644 --- a/frontend/components/parameters/Plannings/PlanningDisplayParameters.jsx +++ b/frontend/components/parameters/Plannings/PlanningDisplayParameters.jsx @@ -5,6 +5,7 @@ import swal from "sweetalert2"; export default function PlanningDisplayParameters() { const [showActivityCode, setShowActivityCode] = React.useState(false); + const [recurrenceActivated, setRecurrenceActivated] = React.useState(false); useEffect(() => { @@ -12,6 +13,7 @@ export default function PlanningDisplayParameters() .success((data) => { setShowActivityCode(data.show_activity_code); + setRecurrenceActivated(data.recurrence_activated); }) .error(() => { @@ -20,7 +22,7 @@ export default function PlanningDisplayParameters() type: "error", }); }) - .get("/parameters/show_activity_code", {}); + .get("/parameters/planning/school_planning_params", {}); }, []); const onSubmit = (e) => @@ -40,7 +42,10 @@ export default function PlanningDisplayParameters() type: "error", }); }) - .post("/parameters/show_activity_code", { show_activity_code: showActivityCode }); + .post("/parameters/planning/school_planning_params", { + show_activity_code: showActivityCode, + recurrence_activated: recurrenceActivated + }); }; return
@@ -49,10 +54,18 @@ export default function PlanningDisplayParameters()
- setShowActivityCode(!showActivityCode)} /> + setShowActivityCode(!showActivityCode)} />
+
+ + setRecurrenceActivated(!recurrenceActivated)} /> + +
+
; diff --git a/frontend/components/planning/CreateActivityModal.jsx b/frontend/components/planning/CreateActivityModal.jsx index b6ba2c7a..44b89314 100755 --- a/frontend/components/planning/CreateActivityModal.jsx +++ b/frontend/components/planning/CreateActivityModal.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { Fragment } from "react"; import PropTypes from "prop-types"; import _ from "lodash"; @@ -6,6 +6,9 @@ const moment = require("moment"); require("moment/locale/fr"); import { getSeasonFromDate } from "./TimeIntervalHelpers"; +import { RECURRENCE_TYPES, WEEKDAYS } from "../../tools/constants"; +import { toFullDateFr, toLocaleDate } from "../../tools/format"; +import Checkbox from "../common/Checkbox"; class CreateIntervalModal extends React.Component { constructor(props) { @@ -16,6 +19,9 @@ class CreateIntervalModal extends React.Component { this.state = { kind: this.props.kind || "p", season: detectedSeason && detectedSeason.id || "", + isAdminSelectIntervalRecurrence: false, + isRecurrent: false, + recurrentType: RECURRENCE_TYPES.getDefault() }; } @@ -26,7 +32,7 @@ class CreateIntervalModal extends React.Component { } handleSave() { - const interval = { ...this.props.newInterval, kind: this.state.kind }; + const interval = { ...this.props.newInterval, kind: this.state.kind, recurrentType: this.state.isRecurrent ? this.state.recurrentType : null }; this.props.onSave(interval, this.state.season); this.props.closeModal(); } @@ -36,13 +42,89 @@ class CreateIntervalModal extends React.Component { } - render() { - return ( -
+ render() + { + let component; + + if (this.props.currentUserIsAdmin && this.props.recurrenceActivated) + { + if (this.state.isAdminSelectIntervalRecurrence) + { + + + component =
+

Création d'une disponibilité

+
+ +
+
+ La disponibilité sera ajoutée pour le créneau suivant :
+ {toFullDateFr(this.props.newInterval.start)} de   + {new Date(this.props.newInterval.start).toLocaleTimeString()} à   + {new Date(this.props.newInterval.end).toLocaleTimeString()} +
+
+ +
+
+ this.setState({ isRecurrent: e.target.checked }) + }} + /> +
+ + {this.state.isRecurrent &&
+ +
} +
+
+ } + else + { + const date = new Date(this.props.newInterval.start); + + component =
+

{toFullDateFr(date)}

+
+ +
this.props.handleCloseAndOpenDetails(this.props.newInterval)}> +
+ +
+
+ Ajout d'un cours +
+
+ +
this.setState({isAdminSelectIntervalRecurrence: true})}> +
+ +
+
+ Ajout d'une disponibilité +
+
+
+ } + } + else + { + component =

Création d'un créneau de disponibilité


- +