main.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137
  1. var majpushed = false;
  2. var selectedDay;
  3. window.planning = null;
  4. //CHARGEMENT DE LA PAGE
  5. function init_plugin_planning(){
  6. switch($.urlParam('page')){
  7. default:
  8. break;
  9. }
  10. $(window).resize(function() {
  11. if(window.planning) window.planning.setOption('height', $(window).height()*0.83);
  12. });
  13. planning_set_type();
  14. if($('.planning-page').attr('data-event-editable')=='0')
  15. $('#planning-event-form').find('input,select,textarea').attr('readonly','readonly');
  16. $( "#planning-datepicker" ).datepicker({
  17. dayNames: ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"],
  18. dayNamesMin: ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"],
  19. dayNamesShort: ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"],
  20. changeMonth: true,
  21. changeYear: true,
  22. monthNames: ["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Aout","Septembre","Octobre","Novembre","Décembre"],
  23. monthNamesShort: ["Jan","Fév","Mars","Avr","Mai","Juin","Juil","Aout","Sept","Oct","Nov","Déc"],
  24. onSelect : function(date,element){
  25. date = date.split('/');
  26. window.planning.gotoDate(date[2]+'-'+date[0]+'-'+date[1]);
  27. }
  28. });
  29. $('#planning-event-form #label').enter(function(){
  30. planning_event_save();
  31. });
  32. planning_search(function(){
  33. planning_calendar_init();
  34. });
  35. $(document).keydown(function(e){
  36. switch(e.keyCode){
  37. //Appuis sur maj
  38. case 16:
  39. majpushed = true;
  40. break;
  41. //Appuis sur suppr
  42. case 46:
  43. var group = $('.event-selected').attr('data-group');
  44. //si le group est vide on supprime uniquement l'event sélectionné
  45. if(!group) return planning_event_delete($('.prime-event-selected'));
  46. //Si un groupe existe on verifie que d'autres evenements sont liés
  47. $.action({
  48. action : 'planning_group_events_search',
  49. group : group
  50. },function(response){
  51. //Si d'autres évenements sont liés, on propose a l'utilisateur de les supprimer
  52. if(response.rows.length > 1){
  53. planning_instance(function(target){
  54. if(target == 'group'){
  55. planning_event_delete($('.event-selected'));
  56. }else{
  57. planning_event_delete($('.prime-event-selected'));
  58. }
  59. });
  60. //Si aucun évenement est lié, on supprime uniquement l'evenement courant
  61. }else{
  62. planning_event_delete($('.prime-event-selected'));
  63. }
  64. });
  65. break;
  66. }
  67. });
  68. //relachage du maj
  69. $(document).keyup(function(e){
  70. if(e.keyCode!=16) return;
  71. majpushed = false;
  72. });
  73. }
  74. function planning_instance(callback){
  75. $('#event-group-modal').modal('show');
  76. $('#event-group-modal .event-group-option li').off('click').click(function(){
  77. $('#event-group-modal').modal('hide');
  78. callback($(this).attr('data-target'));
  79. });
  80. }
  81. function init_setting_planning(){
  82. planning_event_type_search();
  83. window.onbeforeunload = function(e){
  84. if(!planningMenuEdited) return e = null;
  85. return "Certaines modifications sur le menu n'ont pas été sauvgardées.\nÊtes-vous sûr de vouloir quitter la page ?";
  86. };
  87. $( ".planning-types" ).sortable({
  88. handle: ".planning-type-handler",
  89. cancel: ".not-editable",
  90. helper: "clone",
  91. opacity: 0.8,
  92. distance : 15,
  93. grid: [ 50, 1 ],
  94. update : function(){
  95. planningMenuEdited = true;
  96. },
  97. start: function(e,ui) {
  98. draggedItem = $(ui.item[0]);
  99. if(draggedItem.hasClass('planning-line-child')) return;
  100. children = [];
  101. var borderReached = false;
  102. $.each(draggedItem.nextAll('li.planning-line'), function(i, childLine){
  103. var line = $(childLine);
  104. if(borderReached || !line.hasClass('planning-line-child')) {
  105. borderReached = true;
  106. return;
  107. }
  108. children.push(line);
  109. line.addClass('hidden');
  110. });
  111. },
  112. sort: function(event,ui){
  113. var leftLimit = ui.item.hasClass('planning-line-child') ? -20 : 15;
  114. if(ui.position.left < leftLimit){
  115. ui.helper.css('left',leftLimit+'px');
  116. }
  117. if(ui.position.left > 65){
  118. left = 65;
  119. ui.helper.css('left',left+'px');
  120. }
  121. if(ui.item.index()==1){
  122. ui.helper.css('left',leftLimit+'px');
  123. }
  124. },
  125. stop: function(event,ui){
  126. if(ui.position.left>15){
  127. if(!ui.item.hasClass('planning-line-child')) planningMenuEdited = true;
  128. ui.item.addClass('planning-line-child');
  129. }else{
  130. if(ui.item.hasClass('planning-line-child')) planningMenuEdited = true;
  131. ui.item.removeClass('planning-line-child');
  132. }
  133. if(!children.length) return;
  134. $.each(children.reverse(), function(i, row){
  135. $(row).insertAfter(draggedItem).removeClass('hidden');
  136. });
  137. },
  138. placeholder: {
  139. element: function(clone, ui) {
  140. var placeholderRow = $('<li class="planning-item-placeholder"></li>');
  141. /** Lié à la feature de drag&drop de plusieurs lignes à la fois **/
  142. if(!clone.hasClass('planning-line-child')){
  143. var rowNb = 1;
  144. var borderReached = false;
  145. $.each(clone.nextAll('li.planning-line'), function(i, childLine){
  146. var line = $(childLine);
  147. if(borderReached || !line.hasClass('planning-line-child')) {
  148. borderReached = true;
  149. return;
  150. }
  151. rowNb += 1
  152. });
  153. placeholderRow.css({
  154. height: 'calc(44px * '+rowNb+')',
  155. });
  156. }
  157. return placeholderRow;
  158. },
  159. update: function() {
  160. return;
  161. }
  162. }
  163. }).disableSelection();
  164. }
  165. /** PLANNING **/
  166. //Récuperation d'une liste de planning dans le tableau #plannings
  167. function planning_search(callback){
  168. $.action({
  169. action:'planning_search'
  170. },function(r){
  171. var planning = $('#plannings');
  172. var sharedPlanning = $('#shared-plannings');
  173. planning.find('li:visible').remove();
  174. sharedPlanning.find('li:visible').remove();
  175. var tpl = $('li.hidden',planning).get(0).outerHTML;
  176. var options = '';
  177. for(var k in r.rows){
  178. var line = r.rows[k];
  179. options +='<option value="'+line.id+'">'+line.label+'</option>';
  180. var li = $(Mustache.render(tpl,line));
  181. if(line.selected) li.find('input').prop('checked',true);
  182. if(line.shared){
  183. sharedPlanning.append(li);
  184. }else{
  185. planning.append(li);
  186. }
  187. li.removeClass('hidden');
  188. }
  189. $('#event-planning').html(options);
  190. if(callback!=null) callback();
  191. });
  192. }
  193. //Ajout ou modification d'élément planning
  194. function planning_save(){
  195. var data = $('#planning-form').toJson();
  196. data.action = 'planning_save';
  197. $.action(data,function(r){
  198. $.message('success','Enregistré');
  199. $('#planningModal').modal('hide');
  200. planning_search();
  201. });
  202. }
  203. //Récuperation ou edition d'élément planning
  204. function planning_edit(element){
  205. var data = {};
  206. if(element)
  207. data.id = $(element).closest('li').attr('data-id');
  208. $('#planningModal .modal-content').load('action.php?action=planning_edit',data,function(){
  209. init_components('#planningModal .modal-content');
  210. planning_share_search();
  211. });
  212. $('#planningModal').modal('show');
  213. }
  214. function planning_set_type(){
  215. var type = $('#planning-event-type option:selected').val()
  216. var subInput = $('#planning-event-subtype');
  217. $.action({
  218. action : 'planning_subtype_search',
  219. type : type
  220. },function(r){
  221. $('option',subInput).remove();
  222. for(var k in r.rows){
  223. var line = r.rows[k];
  224. subInput.append('<option style="background-color:'+line.color+'" data-icon="'+line.icon+'" value="'+line.id+'">'+line.label+'</option>');
  225. }
  226. init_components(subInput.parent());
  227. });
  228. }
  229. //Suppression d'élement planning
  230. function planning_delete(element){
  231. if(!confirm('Êtes vous sûr de vouloir supprimer cet item ?')) return;
  232. var line = $(element).closest('li');
  233. $.action({
  234. action : 'planning_delete',
  235. id : line.attr('data-id')
  236. },function(r){
  237. line.remove();
  238. window.planning.refetchEvents();
  239. $.message('info','Élement supprimé');
  240. });
  241. }
  242. function planning_select(){
  243. window.planning.refetchEvents();
  244. }
  245. //Récuperation d'une liste de planning dans le tableau #plannings
  246. function planning_calendar_init(callback){
  247. var plannings = [];
  248. var planningMenuEdited = false;
  249. var clickCnt = 0;
  250. var data = $('#planning').data();
  251. window.planning = new FullCalendar.Calendar($('#planning').get(0),{
  252. locale:'fr',
  253. initialView: data.view,
  254. plugins: [ TeammateViewPlugin ],
  255. nextDayThreshold: '00:00:00',
  256. height: $(window).height()*0.83,
  257. navLinks: true, // can click day/week names to navigate views
  258. editable: false,
  259. businessHours : true,
  260. dayMaxEvents: true, // autorise le lien + d'evenement si trop d'evenements
  261. selectable: true,
  262. buttonText : {
  263. today: 'Aujourd\'hui',
  264. month: 'Mois',
  265. week: 'Semaine',
  266. day: 'Jour',
  267. list: 'Liste'
  268. },
  269. customButtons: {
  270. refreshButton: {
  271. text: '',
  272. click: function() {
  273. window.planning.refetchEvents();
  274. }
  275. },
  276. teammateButton: {
  277. text: 'Equipe',
  278. click: function () {
  279. window.planning.changeView('teammate');
  280. }
  281. }
  282. },
  283. headerToolbar: {
  284. left: 'prev,next today refreshButton',
  285. center: 'title',
  286. //right: 'teammateButton,dayGridMonth,timeGridWeek,timeGridDay,listMonth'
  287. right: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth'
  288. },
  289. //lancé lorsque qu'une nouvelle vue est chargée
  290. viewDidMount: function(data) {
  291. //force fullcalendar a recharger les evenements lors du changement de la vue (ie: la vue equipe n'affiche pas les même event que la vue mois)
  292. window.planning.refetchEvents();
  293. //ajout l'icone de refresh au boutton rafraichir
  294. $('.fc-refreshButton-button').html('<i class="fas fa-redo"></i>');
  295. },
  296. //custom d'evenemment en virtual DOM
  297. eventContent: function(arg, createElement) {
  298. /*var innerText
  299. if (arg.event.extendedProps.isUrgent) {
  300. innerText = 'urgent event'
  301. } else {
  302. innerText = 'normal event'
  303. }
  304. return createElement('div', {}, innerText);*/
  305. },
  306. //modif pour chaque event après chargement
  307. eventDidMount: function(data) {
  308. if(data.event.end == null || data.event.start == null) return;
  309. var event = data.event;
  310. var start = moment(event.start);
  311. var end = moment(event.end);
  312. var startDay = start.format("DD/MM/YYYY");
  313. var endDay = end.format("DD/MM/YYYY");
  314. var startSchedule = start.format("HH:mm");
  315. var endSchedule = end.format("HH:mm");
  316. var location = event.extendedProps.location == null ? '' : event.extendedProps.location;
  317. var description = event.extendedProps.description == null ? '' : event.extendedProps.description;
  318. var info = "Du "+startDay+" au "+endDay+"\r"+event.title+"\r"+'De '+startSchedule+' à '+endSchedule+"\r"+description+"\r"+location;
  319. var element = $(data.el);
  320. element.attr("title", info).attr('data-tooltip',event.id).attr('data-id',event.id);
  321. if(event.display=='background') return;
  322. element.find('.fc-event-title').append('<span class="fc-icon right"><i class="'+event.extendedProps.icon+'"></i></span>');
  323. if(event.extendedProps.underlineColor) element.css({
  324. 'border-left':'4px solid '+event.extendedProps.underlineColor,
  325. 'border-bottom-left-radius':'0',
  326. 'border-top-left-radius':'0'
  327. });
  328. //selection de l'event dont l'id est en parametre event si existant
  329. if($.urlParam('event')){
  330. if(event.id==$.urlParam('event')) $('.fc-event[data-id="'+$.urlParam('event')+'"]').addClass('event-selected');
  331. $('#planning').addClass('event-selected-mode');
  332. }
  333. init_tooltips(element.parent());
  334. },
  335. //options spéciales pour chaques vues
  336. views : {
  337. //vue teammate
  338. teammate : {
  339. addTeammate : function(teammates){
  340. $.action({
  341. action : 'planning_teammate_save',
  342. teammates : teammates
  343. },function(response){
  344. window.planning.view.renderTeammates(response.teammates);
  345. window.planning.refetchEvents();
  346. });
  347. },
  348. sortTeammate : function(sort){
  349. $.action({
  350. action : 'planning_teammate_sort',
  351. sort : sort
  352. },function(response){
  353. });
  354. },
  355. highlight: '.fc-teammate-line[data-teammate="'+$('#planning').attr('data-user')+'"]',
  356. dateIncrement: { days: 7 },
  357. visibleRange: function(currentDate) {
  358. currentDate = moment(currentDate);
  359. var startOfMonth = currentDate.clone().startOf('week');
  360. var endOfMonth = currentDate.clone().endOf('week');
  361. return {
  362. start: startOfMonth.clone(),
  363. end: endOfMonth.clone()
  364. };
  365. },
  366. teammates : JSON.parse($('.planning-teammates').html()),
  367. },
  368. },
  369. //Récuperation ajax des évenements
  370. events: {
  371. url: 'action.php?action=planning_event_search',
  372. //parametre ajax dynamic (planning sélectionné, equipiers etc...)
  373. extraParams: function(){
  374. parameters = { plannings : [] };
  375. var teammates = JSON.parse($('.planning-teammates').html());
  376. parameters.teammates = [];
  377. for(var i in teammates){
  378. parameters.teammates.push(teammates[i].login);
  379. }
  380. parameters.view = $('.fc-view-harness > div').attr('class');
  381. if(!parameters.view) return;
  382. parameters.view = $('.fc-view-harness > div').attr('class').match(/fc-([^-]*)-view/i);
  383. parameters.view = parameters.view.length>1 ? parameters.view[1] : '';
  384. $('.planning-list li:visible input:checked').each(function(i,input){
  385. parameters.plannings.push($(input).closest('li').attr('data-id'))
  386. });
  387. return parameters;
  388. },
  389. failure: function() {
  390. $('#script-warning').removeClass('hidden');
  391. }
  392. },
  393. //Appellé lors début/fin de chargement du planning
  394. loading: function(isLoading){
  395. if(isLoading){
  396. $('.fc-refreshButton-button').html($('.planning-loader').html());
  397. }else{
  398. $('.fc-refreshButton-button').html('<i class="fas fa-redo"></i>');
  399. }
  400. },
  401. dateClick: function(data) {
  402. $('prime-event-selected').removeClass('prime-event-selected');
  403. $('.event-selected').removeClass('event-selected');
  404. selectedDay = {
  405. element : $(this),
  406. view : data.view.type,
  407. date : moment(data.date)
  408. };
  409. if($('.planning-page').attr('data-event-editable')=='0') return;
  410. var calculatedEnd = ''+parseInt(selectedDay.date.format("HH"))+1;
  411. var newEvent = {
  412. startDate : selectedDay.date.format("DD/MM/YYYY"),
  413. endDate : selectedDay.date.format("DD/MM/YYYY"),
  414. startHour : selectedDay.date.format("HH"),
  415. endHour : "00".substring(0, 2 - calculatedEnd.length) + ''+ calculatedEnd,
  416. startMinut : selectedDay.date.format("mm"),
  417. endMinut : selectedDay.date.format("mm")
  418. };
  419. switch(selectedDay.view){
  420. case 'teammate':
  421. newEvent.user = selectedDay.element.closest('tr').attr('data-teammate');
  422. if(selectedDay.date.format("HH") == '00'){
  423. var startTime = $('#planning').attr('data-start-hour') ? $('#planning').attr('data-start-hour') : '09:00';
  424. }else{
  425. var startTime = '12:00';
  426. }
  427. startTime = startTime.split(':');
  428. newEvent.startHour = startTime[0];
  429. newEvent.startMinut = startTime[1];
  430. break;
  431. case 'dayGridMonth':
  432. newEvent.startHour = '08';
  433. newEvent.endHour = '12';
  434. newEvent.startMinut = '00';
  435. newEvent.endMinut = '00';
  436. var planningConf = $('#planning').data();
  437. var defaultStart = planningConf.startHour.split(':');
  438. var defaultEnd = planningConf.endHour.split(':');
  439. if(defaultStart.length==2){
  440. newEvent.startHour = defaultStart[0];
  441. newEvent.startMinut = defaultStart[1];
  442. }
  443. if(defaultEnd.length==2){
  444. newEvent.endHour = defaultEnd[0];
  445. newEvent.endMinut = defaultEnd[1];
  446. }
  447. break;
  448. }
  449. var end = moment(newEvent.startDate+' '+newEvent.startHour+':'+newEvent.startMinut, 'DD/MM/YYYY HH:mm').add(2,'hour');
  450. newEvent.endHour = end.format('HH');
  451. newEvent.endMinut = end.format('mm');
  452. planning_event_edit(newEvent,$(data.dayEl));
  453. data.jsEvent.stopPropagation();
  454. },
  455. eventClick: function(data) {
  456. clickCnt++;
  457. //Simple clic, on sélectionne l'event
  458. if(clickCnt === 1){
  459. oneClickTimer = setTimeout(function() {
  460. clickCnt = 0;
  461. if(!majpushed) $('.event-selected').removeClass('event-selected');
  462. var target = $(data.el);
  463. var group = target.attr('data-group');
  464. target.addClass('prime-event-selected');
  465. if(group != null && group != ''){
  466. target = $('[data-group="'+group+'"]');
  467. }else{
  468. target = $('[data-id="'+target.attr('data-id')+'"]');
  469. }
  470. if(!$(data.el).hasClass('event-selected')){
  471. $('#planning').addClass('event-selected-mode');
  472. target.addClass('event-selected');
  473. target.addClass('prime-event-selected');
  474. }else{
  475. $('#planning').removeClass('event-selected-mode');
  476. target.removeClass('event-selected');
  477. target.removeClass('prime-event-selected');
  478. }
  479. }, 400);
  480. //Double clic, on ouvre la modale
  481. }else if(clickCnt === 2){
  482. clearTimeout(oneClickTimer);
  483. clickCnt = 0;
  484. $('prime-event-selected').removeClass('prime-event-selected');
  485. $('.event-selected').removeClass('event-selected');
  486. var start = moment(data.event.start);
  487. var end = moment(data.event.end);
  488. //obligé de copier l'objet car définit comme non extensible par fullcalendar
  489. var props = Object.assign({}, data.event.extendedProps, {selected:false});
  490. var event = $.extend(props,{
  491. id : data.event.id,
  492. label : data.event.title,
  493. startDate : start.format("DD/MM/YYYY"),
  494. endDate : end.format("DD/MM/YYYY"),
  495. startHour : start.format("HH"),
  496. endHour : end.format("HH"),
  497. startMinut : start.format("mm"),
  498. endMinut : end.format("mm")
  499. });
  500. if(data.view.type == 'teammate') event.user = $(this).closest('tr').attr("data-teammate");
  501. planning_event_edit(event,$(data.el).closest('.fc-day'));
  502. data.jsEvent.stopPropagation();
  503. }
  504. },
  505. /* eventMouseEnter: function(options){}, */
  506. eventDrop: function(data) {
  507. if($('.planning-page').attr('data-event-editable')=='0') return;
  508. if(data.event == null) return;
  509. if(data.event.end == null || data.event.start == null) return null;
  510. var event = planning_convert_event(data.event);
  511. $.action({
  512. action : 'planning_event_move',
  513. view : window.planning.view.type,
  514. event : event
  515. });
  516. },
  517. eventResize: function(data ) {
  518. if($('.planning-page').attr('data-event-editable')=='0') return;
  519. if(data.event == null) return;
  520. var event = planning_convert_event(data.event);
  521. planning_event_save(event,function(r){
  522. if(r.error )revertFunc();
  523. });
  524. }
  525. });
  526. window.planning.render();
  527. if($.urlParam('start'))
  528. window.planning.gotoDate($.urlParam('start'));
  529. $('#planning').click(function(){
  530. planning_panel_show(false);
  531. if(!majpushed){
  532. $('.event-selected').removeClass('event-selected');
  533. $('#planning').removeClass('event-selected-mode');
  534. }
  535. });
  536. }
  537. //verifie la coherence date/heure début/fin et corrige au besoin
  538. function planning_checkstart_end(){
  539. if(!$('#startDate').val()) return;
  540. var start = moment($('#startDate').val()+' '+$('#startTime').val(),'DD/MM/YYYY HH:mm');
  541. if(!$('#endDate').val()) $('#endDate').val(start.format('DD/MM/YYYY'));
  542. if(!$('#endTime').val()) $('#endTime').val( moment(start).add(1,'hour').format('HH:mm') );
  543. var end = moment($('#endDate').val()+' '+$('#endTime').val(),'DD/MM/YYYY HH:mm');
  544. if(end<start){
  545. var newEnd = moment(start).add(1,'hour');
  546. $('#endDate').val( newEnd.format('DD/MM/YYYY') );
  547. $('#endTime').val( newEnd.format('HH:mm') );
  548. }
  549. }
  550. //Convertis un evenements fc Calendar en evenement erp
  551. function planning_convert_event(fcEvent){
  552. if(fcEvent.end == null || fcEvent.start == null) return null;
  553. var event = {};
  554. event.id = fcEvent.id;
  555. event.label = fcEvent.title;
  556. var start = moment(fcEvent.start);
  557. var end = moment(fcEvent.end);
  558. event.startDate = start.format("DD/MM/YYYY");
  559. event.endDate = end.format("DD/MM/YYYY");
  560. event.startHour = start.format("HH");
  561. event.endHour = end.format("HH");
  562. event.startMinut = start.format("mm");
  563. event.endMinut = end.format("mm");
  564. $.extend(event,fcEvent.extendedProps);
  565. return event;
  566. }
  567. /** PLANNING EVENT **/
  568. //Ajout ou modification d'élément planningevent
  569. function planning_event_save(data,callback){
  570. if($('.planning-page').attr('data-event-editable')=='0') return;
  571. if(!data){
  572. data = $('#planning-event-form').toJson();
  573. data.type = $('#planning-event-type').val();
  574. data.id = $('#planning-event-form').attr('data-event');
  575. data.planning = $('#event-planning').val();
  576. data.dailyFrequency = $('[name="dailyRepeatFrequency"]:checked').attr('data-value');
  577. data.repeatEndType = $('[name="repeatEndType"]:checked').attr('data-value');
  578. data.resources = planning_event_resource_get();
  579. var startTime = data.startTime.split(':');
  580. var endTime = data.endTime.split(':');
  581. data.startHour = startTime[0];
  582. data.startMinut = startTime[1];
  583. data.endHour = endTime[0];
  584. data.endMinut = endTime[1];
  585. data.weeklyDay = [];
  586. $('[data-type-form="weekly"] input[type="checkbox"]:checked').each(function(){
  587. data.weeklyDay.push($(this).attr('data-value'));
  588. });
  589. }
  590. data.view = window.planning.view.type;
  591. data.action = 'planning_event_save';
  592. $.action(data,function(r){
  593. $('#planning-event-form').attr('data-event','');
  594. planning_panel_show(false);
  595. window.planning.refetchEvents();
  596. if(callback) callback(r);
  597. },function(r){
  598. if(callback) callback(r);
  599. });
  600. }
  601. //gestion ouverture panel avec effet
  602. function planning_panel_show(open){
  603. var panel = $('#planning-event-panel');
  604. if(window.panelTimeout) clearTimeout(window.panelTimeout);
  605. if(window.panelChangeTimeout) clearTimeout(window.panelChangeTimeout);
  606. if(open){
  607. window.panelChangeTimeout = setTimeout(function(){
  608. panel.removeClass('hidden');
  609. window.panelTimeout = setTimeout(function(){
  610. panel.removeClass('fold');
  611. },200);
  612. },50);
  613. }else{
  614. window.panelChangeTimeout = setTimeout(function(){
  615. panel.addClass('fold');
  616. window.panelTimeout = setTimeout(function(){
  617. $('#planning-event-panel').addClass('hidden');
  618. },200);
  619. },50);
  620. }
  621. }
  622. //Récuperation ou edition d'élément planningevent
  623. function planning_event_edit(event,dayElement){
  624. var eventPanel = $('#planning-event-panel');
  625. eventPanel.draggable({
  626. handle: ".planning-event-panel-head",
  627. containment: ".module-planning",
  628. scroll: false
  629. });
  630. var top = 0;
  631. var left = 0;
  632. //positionnement de la modale en fonction de la taille du viewport et de l'élement cliqué
  633. if(dayElement){
  634. var offset = dayElement.offset();
  635. var top = offset.top +1;
  636. var left = offset.left +1;
  637. var viewportWidth = $(window).outerWidth();
  638. var viewportHeight = $(window).outerHeight();
  639. var eventPanelHeight = eventPanel.outerHeight();
  640. var eventPanelWidth = eventPanel.outerWidth();
  641. if(top+eventPanelHeight > viewportHeight)
  642. top-= eventPanelHeight;
  643. if(left+eventPanelWidth > viewportWidth)
  644. left-= eventPanelWidth;
  645. }
  646. eventPanel.css({
  647. top : top+'px',
  648. left : left+'px'
  649. });
  650. eventPanel.removeClass('event-readonly');
  651. //supprime toutes les classes débutant par view-*
  652. eventPanel.removeClass (function (index, className) {
  653. return (className.match (/(^|\s)view-\S+/g) || []).join(' ');
  654. });
  655. if(event.editable===false) eventPanel.addClass('event-readonly');
  656. eventPanel.addClass('view-'+window.planning.view.type);
  657. planning_panel_show(true);
  658. eventPanel.find('#label').focus();
  659. $('#event-advanced').addClass('hidden');
  660. if(event.editable===false){
  661. $('#planning-event-panel').find('input,textarea,select').prop('readonly',true).attr('readonly','readonly');
  662. $('#planning-event-panel select option').attr('disabled', true);
  663. }else{
  664. $('#planning-event-panel').find('input,textarea,select').prop('readonly',false).removeAttr('readonly');
  665. $('#planning-event-panel select option').removeAttr('disabled');
  666. }
  667. $('#planning-event-form').find('input,textarea').val('');
  668. $('#planning-event-form').fromJson(event);
  669. $('#planning-event-form #startTime').val(event.startHour+':'+event.startMinut);
  670. $('#planning-event-form #endTime').val(event.endHour+':'+event.endMinut);
  671. $('#event-repeat-type').val(event.repeatType).trigger('change');
  672. $('#event-repeat-occurences').val(event.repeatOccurence);
  673. $('#event-repeat-until').val(event.repeatUntil)
  674. $('#event-repeat-daily-number').val(event.repeatDailyNumber);
  675. $('#event-repeat-yearly-number').val(event.repeatYearlyNumber);
  676. $('#event-repeat-monthly-number').val(event.repeatMonthlyNumber);
  677. $('#event-repeat-yearly-month').val(event.repeatYearlyMonth);
  678. $('#event-repeat-monthly-day').val(event.repeatMonthlyDay);
  679. $('[data-type-form="weekly"] input[type="checkbox"]').each(function(){
  680. $(this).prop('checked',$.inArray($(this).attr('data-value'),event.repeatWeeklyDay) != -1);
  681. });
  682. $('[name="repeatEndType"][data-value="'+event.repeatEndType+'"]').prop('checked',true);
  683. $('#planning-event-type').val(event.type);
  684. var planning = '';
  685. var checkedPlannings = $('.planning-list li:visible input:checked');
  686. //déduction du planning par défaut ou du planning correspondant a l'event
  687. if(event.planning){
  688. planning = event.planning ;
  689. }else{
  690. $('#planning-event-type .dropdown-menu > a:eq(0)').trigger('click');
  691. //si un planning est apr defaut on le prends de base
  692. planning = $('.planning-list li[data-default="1"]').attr('data-id');
  693. //Si pas de planning par defaut on prend le premier
  694. if(!planning) planning = $('.planning-list li:eq(1)').attr('data-id');
  695. //si un seul planning est coché on prend celui la
  696. if(checkedPlannings.length==1) planning = checkedPlannings.closest('li').attr('data-id');
  697. }
  698. $('#event-planning').val(planning);
  699. $('#planning-event-form').attr('data-event',!event.id? '': event.id);
  700. init_components('#planning-event-form');
  701. if(!$('#event-repeat-type').val()) $('#event-repeat-type').val('never');
  702. planning_event_repeat_change();
  703. if(event.resources) planning_event_resource_load(event.resources);
  704. }
  705. //Suppression d'élement planningevent
  706. function planning_event_delete (elements){
  707. if($('.planning-page').attr('data-event-editable')=='0') return;
  708. var events = [];
  709. elements.each(function(i,event){
  710. if($(event).attr('data-id') != null)
  711. events.push($(event).attr('data-id'));
  712. });
  713. if(events.length == 0) return;
  714. if(!confirm('Êtes vous sûr de vouloir supprimer cet item ?')) return;
  715. $.action({
  716. action : 'planning_event_delete',
  717. events : events
  718. },function(r){
  719. window.planning.refetchEvents();
  720. });
  721. }
  722. /** PLANNING EVENT TYPE **/
  723. //Récuperation d'une liste de planningeventtype dans le tableau #planningeventtypes
  724. function planning_event_type_search(callback){
  725. $('.planning-types').fill({
  726. action:'planning_event_type_search'
  727. },function(){
  728. $('.planning-types .type-editable').each(function(){
  729. $(this).prop('checked',$(this).val()=="true");
  730. });
  731. init_components('.planning-types');
  732. if(callback!=null) callback();
  733. });
  734. }
  735. //Ajout ou modification d'élément event_type
  736. function planning_event_type_save(){
  737. var items = [];
  738. var lastParentIndex = 0;
  739. $('.planning-line').each(function(i,element){
  740. var line = $(element);
  741. if(isNaN(line.attr('data-id'))) return;
  742. var item = {};
  743. item.id = line.attr('data-id');
  744. item.label = line.find('.type-label').val();
  745. item.color = line.find('.type-color').val();
  746. item.icon = line.find('.type-icon').val();
  747. item.editable = line.find('.type-editable').prop('checked') ? 1 :0;
  748. if(line.hasClass('planning-line-child')){
  749. items[lastParentIndex].childs.push(item);
  750. }else{
  751. lastParentIndex = items.length;
  752. item.childs = [];
  753. items.push(item);
  754. }
  755. });
  756. $.action({
  757. action : 'planning_event_type_save',
  758. items : items
  759. },function(){
  760. planningnMenuEdited = false;
  761. planning_event_type_search();
  762. $.message('success','Enregistré');
  763. });
  764. }
  765. function planning_event_type_add(){
  766. var tpl = $('.planning-types li:eq(0)').get(0).outerHTML;
  767. var defaultData = {
  768. label : 'Nouveau type',
  769. color : '#17a2b8',
  770. editable : true,
  771. icon : 'far fa-bookmark'
  772. };
  773. var line = $(Mustache.render(tpl,defaultData));
  774. $('.planning-types').append(line.removeClass('hidden'));
  775. }
  776. //Suppression d'élement event_type
  777. function planning_event_type_delete(element){
  778. if(!confirm('Êtes vous sûr de vouloir supprimer cet item ?')) return;
  779. var line = $(element).closest('li');
  780. line.remove();
  781. }
  782. function planning_event_repeat_change(){
  783. var repeatEventType = $('#event-repeat-type').val();
  784. var eventOption = $('#event-repeat-options');
  785. if(repeatEventType=='never'){
  786. eventOption.addClass('hidden');
  787. }else{
  788. $('[data-type-form]',eventOption).addClass('hidden');
  789. $('[data-type-form="'+repeatEventType+'"]',eventOption).removeClass('hidden');
  790. eventOption.removeClass('hidden');
  791. }
  792. }
  793. /** PLANNING EVENT RESOURCES **/
  794. function planning_event_resource_get(){
  795. var resources = [];
  796. $('.event-resources li:not(:eq(0))').each(function(){
  797. resources.push($(this).find('select').val());
  798. });
  799. return resources;
  800. }
  801. function planning_event_resource_clear(){
  802. $('.event-resources li:not(:eq(0))').remove();
  803. }
  804. function planning_event_resource_load(rows){
  805. planning_event_resource_clear();
  806. for(var k in rows){
  807. var data = rows[k];
  808. planning_event_resource_add(data,true);
  809. }
  810. planning_event_resource_states();
  811. }
  812. function planning_event_resource_add(data,noState){
  813. if(!data) data = {};
  814. var tpl = $('.event-resources li:eq(0)').get(0).outerHTML;
  815. var line = $(tpl);
  816. $('.event-resources').append(line);
  817. line.removeClass('hidden').fromJson(data);
  818. line.find('select').change(function(){
  819. planning_event_resource_states();
  820. });
  821. if(data.resource && !noState) planning_event_resource_states();
  822. }
  823. function planning_event_resource_states(){
  824. var resources = [];
  825. $('#planning-event-form [data-id="resource"]:not(:eq(0))').each(function(){
  826. resources.push($(this).val());
  827. });
  828. var startTime = $('#startTime').val();
  829. var endTime = $('#endTime').val();
  830. if(resources.length==0 || !startTime || !endTime) return;
  831. startTime = startTime.split(':');
  832. endTime = endTime.split(':');
  833. $('.event-resources li:not(:eq(0)) .resource-state i').attr('class',"fas fa-circle-notch fa-spin text-muted")
  834. $('.event-resources li:not(:eq(0)) .resource-state').attr('title','Vérification de la disponibilité');
  835. $.action({
  836. action : 'planning_event_resource_state',
  837. event : $('#planning-event-form').attr('data-event'),
  838. resources : resources,
  839. startDate : $('#startDate').val(),
  840. endDate : $('#endDate').val(),
  841. startHour : startTime[0],
  842. startMinut : startTime[1],
  843. endHour : endTime[0],
  844. endMinut : endTime[1],
  845. },function(response){
  846. $('#planning-event-form [data-id="resource"]:not(:eq(0))').each(function(){
  847. var select = $(this);
  848. var line = select.closest('li');
  849. var className = 'far fa-check-circle text-success';
  850. var label = 'Ressource disponible pour cette periode';
  851. if(response.rows[select.val()] && response.rows[select.val()].length>0){
  852. className = 'far fa-times-circle text-danger';
  853. label = 'Ressource réservée :';
  854. for(var k in response.rows[select.val()]){
  855. var busy = response.rows[select.val()][k];
  856. label += "<br> <i class='fas fa-user-circle'></i> "+busy.creator+" du "+moment.unix(busy.startDate).format("DD/MM/YYYY HH:mm")+" au "+moment.unix(busy.endDate).format("DD/MM/YYYY HH:mm");
  857. }
  858. }
  859. $('.resource-state i',line).attr('class',className);
  860. $('.resource-state',line).attr('title',label);
  861. });
  862. init_components('.event-resources .resource-state');
  863. });
  864. }
  865. /** PLANNINGSHARE **/
  866. //Récuperation d'une liste de planningshare dans le tableau #planningshares
  867. function planning_share_search(callback){
  868. var planning = $('#planning-form').attr('data-id');
  869. if(planning=='') return;
  870. $('#planning-shares').fill({
  871. action:'planning_share_search',
  872. planning : planning,
  873. },function(){
  874. if(callback!=null) callback();
  875. });
  876. }
  877. //Ajout ou modification d'élément planningshare
  878. function planning_share_save(){
  879. var data = $('#planning-share-form').toJson();
  880. data.action='planning_share_save';
  881. data.planning = $('#planning-form').attr('data-id');
  882. data.recipientEntity = $('#recipient').data('values')[0].entity;
  883. if(data.planning=='') return $.message('warning','Veuillez enregistrer une fois le planning avant de créer des partages');
  884. $.action(data,function(r){
  885. planning_share_search();
  886. });
  887. }
  888. //Suppression d'élement planningshare
  889. function planning_share_delete(element){
  890. if(!confirm('Êtes vous sûr de vouloir supprimer cet item ?')) return;
  891. var line = $(element).closest('tr');
  892. $.action({
  893. action : 'planning_share_delete',
  894. id : line.attr('data-id')
  895. },function(r){
  896. line.remove();
  897. $.message('info','Élement supprimé');
  898. });
  899. }
  900. //Enregistrement des configurations
  901. function planning_setting_save(){
  902. $.action({
  903. action : 'planning_setting_save',
  904. fields : $('#planning-setting-form').toJson()
  905. },function(){ $.message('info','Configuration enregistrée'); });
  906. }