Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • feature-type-filters
  • koma/feature/preference-polling-form
  • komasolver
  • main
  • renovate/django-5.x
  • renovate/django_csp-4.x
  • renovate/jsonschema-4.x
  • renovate/uwsgi-2.x
8 results

Target

Select target project
  • konstantin/akplanning
  • matedealer/akplanning
  • kif/akplanning
  • mirco/akplanning
  • lordofthevoid/akplanning
  • voidptr/akplanning
  • xayomer/akplanning-fork
  • mollux/akplanning
  • neumantm/akplanning
  • mmarx/akplanning
  • nerf/akplanning
  • felix_bonn/akplanning
  • sebastian.uschmann/akplanning
13 results
Select Git revision
  • ak-import
  • feature/clear-schedule-button
  • feature/export-filtering
  • feature/json-export-via-rest-framework
  • feature/json-schedule-import-tests
  • feature/preference-polling-form
  • fix/add-room-import-only-once
  • main
  • renovate/django-5.x
  • renovate/django-debug-toolbar-4.x
  • renovate/django-simple-history-3.x
  • renovate/mysqlclient-2.x
12 results
Show changes
!function(e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).dragula=e()}(function(){return function o(r,i,u){function c(t,e){if(!i[t]){if(!r[t]){var n="function"==typeof require&&require;if(!e&&n)return n(t,!0);if(a)return a(t,!0);throw(n=new Error("Cannot find module '"+t+"'")).code="MODULE_NOT_FOUND",n}n=i[t]={exports:{}},r[t][0].call(n.exports,function(e){return c(r[t][1][e]||e)},n,n.exports,o,r,i,u)}return i[t].exports}for(var a="function"==typeof require&&require,e=0;e<u.length;e++)c(u[e]);return c}({1:[function(e,t,n){"use strict";var o={},r="(?:^|\\s)",i="(?:\\s|$)";function u(e){var t=o[e];return t?t.lastIndex=0:o[e]=t=new RegExp(r+e+i,"g"),t}t.exports={add:function(e,t){var n=e.className;n.length?u(t).test(n)||(e.className+=" "+t):e.className=t},rm:function(e,t){e.className=e.className.replace(u(t)," ").trim()}}},{}],2:[function(e,t,n){(function(r){"use strict";var M=e("contra/emitter"),k=e("crossvent"),j=e("./classes"),R=document,q=R.documentElement;function U(e,t,n,o){r.navigator.pointerEnabled?k[t](e,{mouseup:"pointerup",mousedown:"pointerdown",mousemove:"pointermove"}[n],o):r.navigator.msPointerEnabled?k[t](e,{mouseup:"MSPointerUp",mousedown:"MSPointerDown",mousemove:"MSPointerMove"}[n],o):(k[t](e,{mouseup:"touchend",mousedown:"touchstart",mousemove:"touchmove"}[n],o),k[t](e,n,o))}function K(e){if(void 0!==e.touches)return e.touches.length;if(void 0!==e.which&&0!==e.which)return e.which;if(void 0!==e.buttons)return e.buttons;e=e.button;return void 0!==e?1&e?1:2&e?3:4&e?2:0:void 0}function z(e,t){return void 0!==r[t]?r[t]:(q.clientHeight?q:R.body)[e]}function H(e,t,n){var o=(e=e||{}).className||"";return e.className+=" gu-hide",n=R.elementFromPoint(t,n),e.className=o,n}function V(){return!1}function $(){return!0}function G(e){return e.width||e.right-e.left}function J(e){return e.height||e.bottom-e.top}function Q(e){return e.parentNode===R?null:e.parentNode}function W(e){return"INPUT"===e.tagName||"TEXTAREA"===e.tagName||"SELECT"===e.tagName||function e(t){if(!t)return!1;if("false"===t.contentEditable)return!1;if("true"===t.contentEditable)return!0;return e(Q(t))}(e)}function Z(t){return t.nextElementSibling||function(){var e=t;for(;e=e.nextSibling,e&&1!==e.nodeType;);return e}()}function ee(e,t){var t=(n=t).targetTouches&&n.targetTouches.length?n.targetTouches[0]:n.changedTouches&&n.changedTouches.length?n.changedTouches[0]:n,n={pageX:"clientX",pageY:"clientY"};return e in n&&!(e in t)&&n[e]in t&&(e=n[e]),t[e]}t.exports=function(e,t){var l,f,s,d,m,o,r,v,p,h,n;1===arguments.length&&!1===Array.isArray(e)&&(t=e,e=[]);var i,g=null,y=t||{};void 0===y.moves&&(y.moves=$),void 0===y.accepts&&(y.accepts=$),void 0===y.invalid&&(y.invalid=function(){return!1}),void 0===y.containers&&(y.containers=e||[]),void 0===y.isContainer&&(y.isContainer=V),void 0===y.copy&&(y.copy=!1),void 0===y.copySortSource&&(y.copySortSource=!1),void 0===y.revertOnSpill&&(y.revertOnSpill=!1),void 0===y.removeOnSpill&&(y.removeOnSpill=!1),void 0===y.direction&&(y.direction="vertical"),void 0===y.ignoreInputTextSelection&&(y.ignoreInputTextSelection=!0),void 0===y.mirrorContainer&&(y.mirrorContainer=R.body);var w=M({containers:y.containers,start:function(e){e=S(e);e&&C(e)},end:O,cancel:L,remove:X,destroy:function(){c(!0),N({})},canMove:function(e){return!!S(e)},dragging:!1});return!0===y.removeOnSpill&&w.on("over",function(e){j.rm(e,"gu-hide")}).on("out",function(e){w.dragging&&j.add(e,"gu-hide")}),c(),w;function u(e){return-1!==w.containers.indexOf(e)||y.isContainer(e)}function c(e){e=e?"remove":"add";U(q,e,"mousedown",E),U(q,e,"mouseup",N)}function a(e){U(q,e?"remove":"add","mousemove",x)}function b(e){e=e?"remove":"add";k[e](q,"selectstart",T),k[e](q,"click",T)}function T(e){i&&e.preventDefault()}function E(e){var t,n;o=e.clientX,r=e.clientY,1!==K(e)||e.metaKey||e.ctrlKey||(n=S(t=e.target))&&(i=n,a(),"mousedown"===e.type&&(W(t)?t.focus():e.preventDefault()))}function x(e){if(i)if(0!==K(e)){if(!(void 0!==e.clientX&&Math.abs(e.clientX-o)<=(y.slideFactorX||0)&&void 0!==e.clientY&&Math.abs(e.clientY-r)<=(y.slideFactorY||0))){if(y.ignoreInputTextSelection){var t=ee("clientX",e)||0,n=ee("clientY",e)||0;if(W(R.elementFromPoint(t,n)))return}n=i;a(!0),b(),O(),C(n);n=function(e){e=e.getBoundingClientRect();return{left:e.left+z("scrollLeft","pageXOffset"),top:e.top+z("scrollTop","pageYOffset")}}(s);d=ee("pageX",e)-n.left,m=ee("pageY",e)-n.top,j.add(h||s,"gu-transit"),function(){if(l)return;var e=s.getBoundingClientRect();(l=s.cloneNode(!0)).style.width=G(e)+"px",l.style.height=J(e)+"px",j.rm(l,"gu-transit"),j.add(l,"gu-mirror"),y.mirrorContainer.appendChild(l),U(q,"add","mousemove",P),j.add(y.mirrorContainer,"gu-unselectable"),w.emit("cloned",l,s,"mirror")}(),P(e)}}else N({})}function S(e){if(!(w.dragging&&l||u(e))){for(var t=e;Q(e)&&!1===u(Q(e));){if(y.invalid(e,t))return;if(!(e=Q(e)))return}var n=Q(e);if(n)if(!y.invalid(e,t))if(y.moves(e,n,t,Z(e)))return{item:e,source:n}}}function C(e){var t,n;t=e.item,n=e.source,("boolean"==typeof y.copy?y.copy:y.copy(t,n))&&(h=e.item.cloneNode(!0),w.emit("cloned",h,e.item,"copy")),f=e.source,s=e.item,v=p=Z(e.item),w.dragging=!0,w.emit("drag",s,f)}function O(){var e;w.dragging&&_(e=h||s,Q(e))}function I(){a(!(i=!1)),b(!0)}function N(e){var t,n;I(),w.dragging&&(t=h||s,n=ee("clientX",e)||0,e=ee("clientY",e)||0,(e=B(H(l,n,e),n,e))&&(h&&y.copySortSource||!h||e!==f)?_(t,e):(y.removeOnSpill?X:L)())}function _(e,t){var n=Q(e);h&&y.copySortSource&&t===f&&n.removeChild(s),A(t)?w.emit("cancel",e,f,f):w.emit("drop",e,t,f,p),Y()}function X(){var e,t;w.dragging&&((t=Q(e=h||s))&&t.removeChild(e),w.emit(h?"cancel":"remove",e,t,f),Y())}function L(e){var t,n,o;w.dragging&&(t=0<arguments.length?e:y.revertOnSpill,!1===(e=A(o=Q(n=h||s)))&&t&&(h?o&&o.removeChild(h):f.insertBefore(n,v)),e||t?w.emit("cancel",n,f,f):w.emit("drop",n,o,f,p),Y())}function Y(){var e=h||s;I(),l&&(j.rm(y.mirrorContainer,"gu-unselectable"),U(q,"remove","mousemove",P),Q(l).removeChild(l),l=null),e&&j.rm(e,"gu-transit"),n&&clearTimeout(n),w.dragging=!1,g&&w.emit("out",e,g,f),w.emit("dragend",e),f=s=h=v=p=n=g=null}function A(e,t){t=void 0!==t?t:l?p:Z(h||s);return e===f&&t===v}function B(t,n,o){for(var r=t;r&&!function(){if(!1===u(r))return!1;var e=D(r,t),e=F(r,e,n,o);if(A(r,e))return!0;return y.accepts(s,r,f,e)}();)r=Q(r);return r}function P(e){if(l){e.preventDefault();var t=ee("clientX",e)||0,n=ee("clientY",e)||0,o=t-d,r=n-m;l.style.left=o+"px",l.style.top=r+"px";var i=h||s,e=H(l,t,n),o=B(e,t,n),u=null!==o&&o!==g;!u&&null!==o||(g&&a("out"),g=o,u&&a("over"));r=Q(i);if(o!==f||!h||y.copySortSource){var c,e=D(o,e);if(null!==e)c=F(o,e,t,n);else{if(!0!==y.revertOnSpill||h)return void(h&&r&&r.removeChild(i));c=v,o=f}(null===c&&u||c!==i&&c!==Z(i))&&(p=c,o.insertBefore(i,c),w.emit("shadow",i,o,f))}else r&&r.removeChild(i)}function a(e){w.emit(e,i,g,f)}}function D(e,t){for(var n=t;n!==e&&Q(n)!==e;)n=Q(n);return n===q?null:n}function F(r,t,i,u){var c="horizontal"===y.direction;return(t!==r?function(){var e=t.getBoundingClientRect();if(c)return n(i>e.left+G(e)/2);return n(u>e.top+J(e)/2)}:function(){var e,t,n,o=r.children.length;for(e=0;e<o;e++){if(t=r.children[e],n=t.getBoundingClientRect(),c&&n.left+n.width/2>i)return t;if(!c&&n.top+n.height/2>u)return t}return null})();function n(e){return e?Z(t):t}}}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./classes":1,"contra/emitter":5,crossvent:6}],3:[function(e,t,n){t.exports=function(e,t){return Array.prototype.slice.call(e,t)}},{}],4:[function(e,t,n){"use strict";var o=e("ticky");t.exports=function(e,t,n){e&&o(function(){e.apply(n||null,t||[])})}},{ticky:10}],5:[function(e,t,n){"use strict";var c=e("atoa"),a=e("./debounce");t.exports=function(r,e){var i=e||{},u={};return void 0===r&&(r={}),r.on=function(e,t){return u[e]?u[e].push(t):u[e]=[t],r},r.once=function(e,t){return t._once=!0,r.on(e,t),r},r.off=function(e,t){var n=arguments.length;if(1===n)delete u[e];else if(0===n)u={};else{e=u[e];if(!e)return r;e.splice(e.indexOf(t),1)}return r},r.emit=function(){var e=c(arguments);return r.emitterSnapshot(e.shift()).apply(this,e)},r.emitterSnapshot=function(o){var e=(u[o]||[]).slice(0);return function(){var t=c(arguments),n=this||r;if("error"===o&&!1!==i.throws&&!e.length)throw 1===t.length?t[0]:t;return e.forEach(function(e){i.async?a(e,t,n):e.apply(n,t),e._once&&r.off(o,e)}),r}},r}},{"./debounce":4,atoa:3}],6:[function(n,o,e){(function(r){"use strict";var i=n("custom-event"),u=n("./eventmap"),c=r.document,e=function(e,t,n,o){return e.addEventListener(t,n,o)},t=function(e,t,n,o){return e.removeEventListener(t,n,o)},a=[];function l(e,t,n){t=function(e,t,n){var o,r;for(o=0;o<a.length;o++)if((r=a[o]).element===e&&r.type===t&&r.fn===n)return o}(e,t,n);if(t){n=a[t].wrapper;return a.splice(t,1),n}}r.addEventListener||(e=function(e,t,n){return e.attachEvent("on"+t,function(e,t,n){var o=l(e,t,n)||function(n,o){return function(e){var t=e||r.event;t.target=t.target||t.srcElement,t.preventDefault=t.preventDefault||function(){t.returnValue=!1},t.stopPropagation=t.stopPropagation||function(){t.cancelBubble=!0},t.which=t.which||t.keyCode,o.call(n,t)}}(e,n);return a.push({wrapper:o,element:e,type:t,fn:n}),o}(e,t,n))},t=function(e,t,n){n=l(e,t,n);if(n)return e.detachEvent("on"+t,n)}),o.exports={add:e,remove:t,fabricate:function(e,t,n){var o=-1===u.indexOf(t)?new i(t,{detail:n}):function(){var e;c.createEvent?(e=c.createEvent("Event")).initEvent(t,!0,!0):c.createEventObject&&(e=c.createEventObject());return e}();e.dispatchEvent?e.dispatchEvent(o):e.fireEvent("on"+t,o)}}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./eventmap":7,"custom-event":8}],7:[function(e,r,t){(function(e){"use strict";var t=[],n="",o=/^on/;for(n in e)o.test(n)&&t.push(n.slice(2));r.exports=t}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],8:[function(e,n,t){(function(e){var t=e.CustomEvent;n.exports=function(){try{var e=new t("cat",{detail:{foo:"bar"}});return"cat"===e.type&&"bar"===e.detail.foo}catch(e){}}()?t:"undefined"!=typeof document&&"function"==typeof document.createEvent?function(e,t){var n=document.createEvent("CustomEvent");return t?n.initCustomEvent(e,t.bubbles,t.cancelable,t.detail):n.initCustomEvent(e,!1,!1,void 0),n}:function(e,t){var n=document.createEventObject();return n.type=e,t?(n.bubbles=Boolean(t.bubbles),n.cancelable=Boolean(t.cancelable),n.detail=t.detail):(n.bubbles=!1,n.cancelable=!1,n.detail=void 0),n}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],9:[function(e,t,n){var o,r,t=t.exports={};function i(){throw new Error("setTimeout has not been defined")}function u(){throw new Error("clearTimeout has not been defined")}function c(t){if(o===setTimeout)return setTimeout(t,0);if((o===i||!o)&&setTimeout)return o=setTimeout,setTimeout(t,0);try{return o(t,0)}catch(e){try{return o.call(null,t,0)}catch(e){return o.call(this,t,0)}}}!function(){try{o="function"==typeof setTimeout?setTimeout:i}catch(e){o=i}try{r="function"==typeof clearTimeout?clearTimeout:u}catch(e){r=u}}();var a,l=[],f=!1,s=-1;function d(){f&&a&&(f=!1,a.length?l=a.concat(l):s=-1,l.length&&m())}function m(){if(!f){var e=c(d);f=!0;for(var t=l.length;t;){for(a=l,l=[];++s<t;)a&&a[s].run();s=-1,t=l.length}a=null,f=!1,function(t){if(r===clearTimeout)return clearTimeout(t);if((r===u||!r)&&clearTimeout)return r=clearTimeout,clearTimeout(t);try{r(t)}catch(e){try{return r.call(null,t)}catch(e){return r.call(this,t)}}}(e)}}function v(e,t){this.fun=e,this.array=t}function p(){}t.nextTick=function(e){var t=new Array(arguments.length-1);if(1<arguments.length)for(var n=1;n<arguments.length;n++)t[n-1]=arguments[n];l.push(new v(e,t)),1!==l.length||f||c(m)},v.prototype.run=function(){this.fun.apply(null,this.array)},t.title="browser",t.browser=!0,t.env={},t.argv=[],t.version="",t.versions={},t.on=p,t.addListener=p,t.once=p,t.off=p,t.removeListener=p,t.removeAllListeners=p,t.emit=p,t.prependListener=p,t.prependOnceListener=p,t.listeners=function(e){return[]},t.binding=function(e){throw new Error("process.binding is not supported")},t.cwd=function(){return"/"},t.chdir=function(e){throw new Error("process.chdir is not supported")},t.umask=function(){return 0}},{}],10:[function(e,n,t){(function(t){var e="function"==typeof t?function(e){t(e)}:function(e){setTimeout(e,0)};n.exports=e}).call(this,e("timers").setImmediate)},{timers:11}],11:[function(a,e,l){(function(e,t){var o=a("process/browser.js").nextTick,n=Function.prototype.apply,r=Array.prototype.slice,i={},u=0;function c(e,t){this._id=e,this._clearFn=t}l.setTimeout=function(){return new c(n.call(setTimeout,window,arguments),clearTimeout)},l.setInterval=function(){return new c(n.call(setInterval,window,arguments),clearInterval)},l.clearTimeout=l.clearInterval=function(e){e.close()},c.prototype.unref=c.prototype.ref=function(){},c.prototype.close=function(){this._clearFn.call(window,this._id)},l.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},l.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},l._unrefActive=l.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;0<=t&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},l.setImmediate="function"==typeof e?e:function(e){var t=u++,n=!(arguments.length<2)&&r.call(arguments,1);return i[t]=!0,o(function(){i[t]&&(n?e.apply(null,n):e.call(null),l.clearImmediate(t))}),t},l.clearImmediate="function"==typeof t?t:function(e){delete i[e]}}).call(this,a("timers").setImmediate,a("timers").clearImmediate)},{"process/browser.js":9,timers:11}]},{},[2])(2)});
\ No newline at end of file
{% extends "admin/base_site.html" %}
{% load tags_AKModel %}
{% load i18n %}
{% load l10n %}
{% load tz %}
{% load static %}
{% load tags_AKPlan %}
{% load fontawesome_6 %}
{% block title %}{% trans "Constraint Violations for" %} {{event}}{% endblock %}
{% block extrahead %}
{{ block.super }}
<script type="application/javascript" src="{% static "common/js/api.js" %}"></script>
<script type="application/javascript" src="{% static "AKScheduling/js/scheduling.js" %}"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const url = "{% url "model:scheduling-constraint-violations-list" event_slug=event.slug %}";
const callback_success = function(response) {
let table_html = '';
let unresolved_constraint_violations = 0;
if(response.length > 0) {
// Update violations table
for(let i=0;i<response.length;i++) {
if(response[i].manually_resolved) {
table_html += '<tr class="text-muted"><td class="nowrap">{% fa6_icon "check" "fas" %}</td>';
}
else {
table_html += '<tr><td></td>';
unresolved_constraint_violations++;
}
table_html += "<td>" + response[i].level_display + "</td><td>" + response[i].type_display + "</td><td>" + response[i].details + "</td><td class='nowrap'>" + response[i].timestamp_display + "</td><td><a href='" + response[i].edit_url + "'><i class='btn btn-primary fa fa-pen'></i></a></td></tr>";
}
}
else {
// Update violations table
table_html ='<tr class="text-muted"><td colspan="5" class="text-center">{% trans "No violations" %}</td></tr>'
}
// Update violation count badge
if(unresolved_constraint_violations > 0)
$('#violationCountBadge').html(unresolved_constraint_violations).removeClass('bg-success').addClass('bg-warning');
else
$('#violationCountBadge').html(0).removeClass('bg-warning').addClass('bg-success');
// Show violation list (potentially empty) in violations table
$('#violationsTableBody').html(table_html);
}
// (Re-)Load constraint violations using AJAX and visualize using violation count badge and violation table
function reload() {
loadCVs(url, callback_success, default_cv_callback_error)
}
reload();
// Bind reload button
$('#btnReloadNow').click(reload);
// Toggle automatic reloading (every 30 s) based on checkbox
let autoReloadInterval = undefined;
$('#cbxAutoReload').change(function () {
if(this.checked) {
autoReloadInterval = setInterval(reload, 30*1000);
}
else {
if(autoReloadInterval !== undefined)
clearInterval(autoReloadInterval);
}
});
});
</script>
{% endblock extrahead %}
{% block content %}
<h4 class="mt-4 mb-4"><span id="violationCountBadge" class="badge bg-success">0</span> {% trans "Violation(s)" %}</h4>
<input type="checkbox" id="cbxAutoReload">
<label for="cbxAutoReload">{% trans "Auto reload?" %}</label>
<br>
<a href="#" id="btnReloadNow" class="btn btn-info">{% fa6_icon "sync-alt" "fas" %} {% trans "Reload now" %}</a>
<table class="table table-striped mt-4 mb-4">
<thead>
<tr>
<th></th>
<th>{% trans "Violation" %}</th>
<th>{% trans "Problem" %}</th>
<th>{% trans "Details" %}</th>
<th>{% trans "Since" %}</th>
<th></th>
</tr>
</thead>
<tbody id="violationsTableBody">
<tr class="text-muted">
<td colspan="5" class="text-center">
{% trans "No violations" %}
</td>
</tr>
</tbody>
</table>
<a href="{% url 'admin:event_status' event.slug %}">{% trans "Event Status" %}</a>
&middot;
<a href="{% url 'admin:schedule' event.slug %}">{% trans "Scheduling" %}</a>
{% endblock %}
{% extends "admin/base_site.html" %}
{% load django_bootstrap5 %}
{% load i18n %}
{% load l10n %}
{% load tz %}
{% load static %}
{% load fontawesome_6 %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<div class="text-center text-md-center">
<h5>
{% if previous_ak %}
<a href="{% url "admin:enter-interest" event.slug previous_ak.pk %}" class="pull-left">&lt;-{{ previous_ak.name }}</a> |
{% endif %}
{% if next_ak %}
<a href="{% url "admin:enter-interest" event.slug next_ak.pk %}">{{ next_ak.name }} -&gt;</a>
{% endif %}
</h5>
</div>
<h4>{{ ak.name }}</h4>
<h5>{{ ak.short_name }}</h5>
<div class="mb-3">
<form method="POST" class="post-form">{% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="save btn btn-primary float-end">
{% fa6_icon "check" 'fas' %} {% trans "Submit" %}
</button>
</form>
</div>
{% for category, aks in categories_with_aks %}
<h5 class="mt-4">{{ category.name }}</h5>
{% for link_ak in aks %}
<a href="{% url "admin:enter-interest" event.slug link_ak.pk %}">{{ link_ak.name }}</a> &middot;
{% endfor %}
{% endfor %}
{% endblock %}
{% extends "admin/base_site.html" %}
{% load tags_AKModel %}
{% load i18n %}
{% load l10n %}
{% load tz %}
{% load static %}
{% load tags_AKPlan %}
{% load fontawesome_6 %}
{% block title %}{% trans "Scheduling for" %} {{event}}{% endblock %}
{% block extrahead %}
{{ block.super }}
<script src="{% static "common/vendor/sortable/Sortable.min.js" %}"></script>
<script src="{% static "common/vendor/sortable/jquery-sortable.js" %}"></script>
<script src="{% static "AKScheduling/vendor/dragula/dragula.js" %}"></script>
<style>
.ak-list {
padding-left: 5px;
user-select: none;
height: 100%;
}
.ak-list > li {
cursor: move;
}
.track-delete {
cursor: pointer;
}
.card-header {
cursor: move;
}
.card {
padding:0!important;
}
</style>
<link rel="stylesheet" href="{% static "AKScheduling/vendor/dragula/dragula.css" %}">
<script>
document.addEventListener('DOMContentLoaded', function () {
// CSRF Protection/Authentication
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
function mark_dirty(container) {
container.removeClass("border-success").addClass("border-warning")
}
function mark_clean(container) {
container.removeClass("border-warning").addClass("border-success");
}
function update_ak_track(ak_id, track_id, container) {
if(container!==undefined)
mark_dirty(container);
$.ajax({
url: "{% url "model:AK-list" event_slug=event.slug %}" + ak_id + "/",
type: 'PATCH',
data: {
track: track_id,
},
success: function (response) {
if(container!==undefined)
mark_clean(container);
},
error: function (response) {
alert("ERROR. Did not update " + changeInfo.event.title)
}
});
}
sortable_options = {
"group": 'ak-lists',
'sort': false,
// Element is dropped into the list from another list
onAdd: function (/**Event*/evt) {
// same properties as onEnd
var ak_id = evt.item.dataset["akId"];
// For lists that should stay in sync with server (all except 'without track')
if(evt.to.dataset["sync"]==="true") {
var container = $(evt.to).parents(".track-container");
var track_id = evt.to.dataset["trackId"];
update_ak_track(ak_id, track_id, container);
}
else {
update_ak_track(ak_id, "", undefined);
}
},
};
$('.ak-list').sortable(sortable_options);
// Add a new track container (and make usable for dragging)
$('#btn-add-track').click(function () {
var new_track_name = prompt("{% trans 'Name of new ak track' %}");
$.ajax({
url: "{% url "model:AKTrack-list" event_slug=event.slug %}",
type: 'POST',
data: {
name: new_track_name,
event: "{{ event.pk }}"
},
success: function (response) {
console.log(response);
$('<div class="card border-success mb-3 track-container" style="width: 20rem;margin-right:20px;margin-bottom: 20px;"><div class="card-header"><span class="btn btn-danger float-end track-delete" data-track-id="' + response["id"] + '">{% fa6_icon "trash" "fas" %}</span><input class="track-name" data-track-id="None" type="text" value="' + response["name"] + '"></div><div class="card-body"><ul data-track-id="' + response["id"] + '" data-name="' + response["name"] + '" data-sync="true" class="ak-list"></ul></div></div>')
.appendTo($("#workspace"))
.find("ul").sortable(sortable_options)
},
error: function (response) {
console.error(response);
alert("{% trans 'Could not create ak track' %}");
}
});
});
$('#workspace')
// React to track name changes
.on('change', '.track-name', function () {
var track_name_field = $(this);
var new_track_name = track_name_field.val();
var track_id = track_name_field.attr("data-track-id");
var container = track_name_field.parents(".track-container")
mark_dirty(container);
$.ajax({
url: "{% url "model:AKTrack-list" event_slug=event.slug %}" + track_id + "/",
type: 'PATCH',
data: {
name: new_track_name,
},
success: function (response) {
console.log(response);
mark_clean(container);
},
error: function (response) {
console.error(response);
alert("{% trans 'Could not update ak track name' %}");
}
});
})
// Allow to delete a track
.on('click', '.track-delete', function () {
if(confirm("{% trans 'Do you really want to delete this ak track?' %}")) {
var track_delete_button = $(this);
var track_id = track_delete_button.data("trackId");
$.ajax({
url: "{% url "model:AKTrack-list" event_slug=event.slug %}" + track_id + "/",
type: 'DELETE',
data: {},
success: function (response) {
console.log(response);
track_delete_button.parents(".track-container").remove();
},
error: function (response) {
console.error(response);
alert("{% trans 'Could not delete ak track' %}");
}
});
}
});
// Make track containers sortable (when dragging the headers)
dragula([$('#workspace')[0]], {
moves: function (el, container, handle) {
return handle.classList.contains('card-header');
}
});
});
</script>
{% endblock extrahead %}
{% block content %}
<div class="mb-5">
<h3>{{ event }}: {% trans "Manage AK Tracks" %}</h3>
<a id="btn-add-track" href="#" class="btn btn-primary">{% fa6_icon "plus" "fas" %} {% trans "Add ak track" %}</a>
</div>
<div id="workspace" class="row" style="">
<div class="card border-primary mb-3" style="width: 20rem;margin-right:20px;margin-bottom: 20px;">
<div class="card-header">{% trans "AKs without track" %}</div>
<div class="card-body">
<ul data-id="None" data-sync="false" class="ak-list">
{% for ak in aks_without_track %}
<li data-ak-id="{{ ak.pk }}" data-bs-toggle="tooltip" data-placement="top" title="">
{{ ak.name }} <span style="color:{{ ak.category.color }}">({{ ak.category }})</span>
</li>
{% endfor %}
</ul>
</div>
</div>
{% for track in tracks %}
<div class="card border-success mb-3 track-container" style="width: 20rem;margin-right:20px;margin-bottom: 20px;">
<div class="card-header">
<span class="btn btn-danger float-end track-delete" data-track-id="{{ track.pk }}">
{% fa6_icon "trash" "fas" %}
</span>
<input class="track-name" data-track-id="{{ track.pk }}" type="text" value="{{ track }}">
</div>
<div class="card-body">
<ul data-track-id="{{ track.pk }}" data-name="{{ track }}" data-sync="true" class="ak-list">
{% for ak in track.aks_with_category %}
<li data-ak-id="{{ ak.pk }}" data-bs-toggle="tooltip" data-placement="top" title="">
{{ ak.name }} <span style="color:{{ ak.category.color }}">({{ ak.category }})</span>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endfor %}
</div>
<a href="{% url 'admin:event_status' event.slug %}">{% trans "Event Status" %}</a>
{% endblock %}
{% load compress %}
{% load tags_AKModel %}
{% load tags_AKPlan %}
{% load i18n %}
{% load l10n %}
{% load tz %}
{% load static %}
{% load django_bootstrap5 %}
{% load fontawesome_6 %}
{% load tags_AKModel %}
{% get_current_language as LANGUAGE_CODE %}
{% localize on %}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}{% trans "Scheduling for" %} {{event}}{% endblock %}</title>
{# Load Bootstrap CSS and JavaScript as well as font awesome #}
{% compress css %}
<link rel="stylesheet" type="text/x-scss" href="{% static 'common/vendor/bootswatch-lumen/theme.scss' %}">
{% fontawesome_6_css %}
<link rel="stylesheet" href="{% static 'common/css/custom.css' %}">
{% endcompress %}
{% compress js %}
{% bootstrap_javascript %}
<script src="{% static 'common/vendor/jquery/jquery-3.6.3.min.js' %}"></script>
{% fontawesome_6_js %}
{% endcompress %}
{% include "AKModel/load_fullcalendar.html" %}
<style>
.unscheduled-slot {
cursor: move;
}
.fc-v-event, .tooltip {
white-space: pre-line;
}
.fc-v-event {
border-width: 4px;
}
html, body {
height: 100%;
margin: 0;
}
.box {
display: flex;
flex-flow: column;
height: 100%;
}
.box .row.header, .box .row.footer {
flex: 0 1 auto;
}
.box .row.content {
flex: 1 1 auto;
}
</style>
<script type="application/javascript" src="{% static "common/js/api.js" %}"></script>
<script type="application/javascript" src="{% static "AKScheduling/js/scheduling.js" %}"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
// Place slots by dropping placeholders on calendar
var containerEl = document.getElementById('unscheduled-slots');
new FullCalendar.Draggable(containerEl, {
itemSelector: '.unscheduled-slot',
});
// Calendar
var planEl = document.getElementById('planCalendar');
plan = new FullCalendar.Calendar(planEl, {
timeZone: '{{ event.timezone }}',
headerToolbar: {
left: 'today prev,next',
center: 'title',
right: 'resourceTimelineDayVert,resourceTimelineDayHoriz,resourceTimelineEventVert,resourceTimelineEventHoriz'
},
//aspectRatio: 2,
height: '100%',
themeSystem: 'bootstrap5',
buttonIcons: {
prev: 'ignore fa-solid fa-angle-left',
next: 'ignore fa-solid fa-angle-right',
},
// Adapt to user selected locale
locale: '{{ LANGUAGE_CODE }}',
initialView: 'resourceTimelineEventVert',
views: {
resourceTimelineDayHoriz: {
type: 'resourceTimelineDay',
buttonText: '{% trans "Day (Horizontal)" %}',
slotDuration: '00:15',
scrollTime: '08:00',
titleFormat: {weekday: 'long', day: 'numeric', month: 'numeric'},
},
resourceTimelineDayVert: {
type: 'resourceTimeGridDay',
buttonText: '{% trans "Day (Vertical)" %}',
slotDuration: '00:30',
scrollTime: '08:00',
titleFormat: {weekday: 'long', day: 'numeric', month: 'numeric'},
},
resourceTimelineEventHoriz: {
type: 'resourceTimeline',
visibleRange: {
start: '{{ event.start | timezone:event.timezone | date:"Y-m-d H:i:s" }}',
end: '{{ event.end | timezone:event.timezone | date:"Y-m-d H:i:s"}}',
},
buttonText: '{% trans "Event (Horizontal)" %}',
slotDuration: '00:15',
},
resourceTimelineEventVert: {
type: 'resourceTimeGrid',
visibleRange: {
start: '{{ event.start | timezone:event.timezone | date:"Y-m-d H:i:s" }}',
end: '{{ event.end | timezone:event.timezone | date:"Y-m-d H:i:s"}}',
},
buttonText: '{% trans "Event (Vertical)" %}',
slotDuration: '00:30',
}
},
// Show full AK title as tooltip for each AK (needs to be removed and newly placed when AK is moved)
eventDidMount: function (info) {
if (info.event.extendedProps.description !== undefined) {
$(info.el).tooltip({title: info.event.extendedProps.description, trigger: 'hover'});
}
},
eventWillUnmount: function (info) {
$(info.el).tooltip('dispose');
},
// React to event changes (moving or change of duration)
eventChange: updateEvent,
eventReceive: updateEvent,
editable: true,
selectable: true,
drop: function (info) {
info.draggedEl.parentNode.removeChild(info.draggedEl);
},
select: function (info) {
console.log(info);
$('#id_start').val(info.startStr);
$('#id_end').val(info.endStr);
$('#id_room').val(info.resource._resource.id);
$('#id_room_name').val(info.resource._resource.title);
$('#id_duration').val(Math.abs(info.end-info.start)/1000/60/60);
$('#id_ak').val("");
$('#newAKSlotModal').modal('show');
},
allDaySlot: false,
nowIndicator: true,
now: "{% timestamp_now event.timezone %}",
eventTextColor: '#fff',
eventColor: '#127ba3',
eventBackgroundColor: '#28B62C',
datesAboveResources: true,
resourceAreaHeaderContent: '{% trans "Room" %}',
resources: '{% url "model:scheduling-resources-list" event_slug=event.slug %}',
eventSources: [
'{% url "model:scheduling-events" event_slug=event.slug %}',
'{% url "model:scheduling-room-availabilities" event_slug=event.slug %}',
'{% url "model:scheduling-default-slots" event_slug=event.slug %}'
],
schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source',
dayMinWidth: 100,
});
plan.setOption('contentHeight', $(window).height() - $('#header').height() * 11);
plan.render();
function updateEvent(changeInfo) {
room = changeInfo.event.getResources()[0];
$.ajax({
url: '{% url "model:scheduling-event-list" event_slug=event.slug %}' + changeInfo.event.extendedProps.slotID + "/",
type: 'PUT',
data: {
start: plan.formatIso(changeInfo.event.start),
end: plan.formatIso(changeInfo.event.end),
roomId: room.id,
},
success: function (response) {
},
error: function (response) {
changeInfo.revert();
alert("ERROR. Did not update " + changeInfo.event.title)
}
});
}
$('.unscheduled-slot').each(function() {
$(this).tooltip({title: $(this).first().attr('data-details'), trigger: 'hover'});
});
const cv_url = "{% url "model:scheduling-constraint-violations-list" event_slug=event.slug %}";
const cv_callback_success = function(response) {
let table_html = '';
let unresolved_violations_count = 0;
if(response.length > 0) {
// Update violations table
for(let i=0;i<response.length;i++) {
let icon_html = '';
let muted_html = '';
if(response[i].manually_resolved) {
icon_html = '{% fa6_icon "check" "fas" %} ';
muted_html = 'text-muted';
}
else {
unresolved_violations_count++;
}
if(response[i].level_display==='{% trans "Violation" %}')
icon_html += '{% fa6_icon "exclamation-triangle" "fas" %}';
else
icon_html += '{% fa6_icon "info-circle" "fas" %}';
table_html += '<tr class="'+ muted_html+ '"><td class="nowrap">' + icon_html;
table_html += "</td><td class='small'>" + response[i].type_display + "</td></tr>";
table_html += "<tr class='" + muted_html + "'><td colspan='2' class='small'>" + response[i].details + "</td></tr>"
}
}
else {
// Update violations table
table_html ='<tr class="text-muted"><td colspan="2" class="text-center">{% trans "No violations" %}</td></tr>'
}
// Update violation count badge
if(unresolved_violations_count > 0)
$('#violationCountBadge').html(unresolved_violations_count).removeClass('bg-success').addClass('bg-warning');
else
$('#violationCountBadge').html(0).removeClass('bg-warning').addClass('bg-success');
// Show violation list (potentially empty) in violations table
$('#violationsTableBody').html(table_html);
}
function reloadCVs() {
loadCVs(cv_url, cv_callback_success, default_cv_callback_error);
}
reloadCVs();
const reloadBtn = $('#reloadBtn');
function reload() {
plan.refetchEvents();
reloadCVs();
// TODO Reload unscheduled AKs
}
reloadBtn.click(reload);
function addSlot() {
let ak = $('#id_ak').val();
if(ak === "") {
alert("{% trans "Please choose AK" %}");
}
else {
$.ajax({
url: "{% url "model:AKSlot-list" event_slug=event.slug %}",
type: 'POST',
data: {
start: $('#id_start').val(),
duration: $('#id_duration').val(),
room: $('#id_room').val(),
ak: ak,
event: "{{ event.pk }}",
treat_as_local: true,
},
success: function (response) {
$('#newAKSlotModal').modal('hide');
reload();
},
error: function (response) {
console.error(response);
alert("{% trans 'Could not create slot' %}");
}
});
}
}
$('#newAKSlotModalSubmitButton').click(addSlot);
});
</script>
</head>
<body>
<div class="modal" id="newAKSlotModal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{% trans "Add slot" %}</h5>
</div>
<div class="modal-body">
<form>
{% bootstrap_form akSlotAddForm %}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" id="newAKSlotModalSubmitButton">{% trans "Add" %}</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
</div>
</div>
</div>
</div>
<div class="box p-3">
<div class="row header pb-2">
<div class="col">
<h2 class="d-inline">
<button class="btn btn-outline-warning" id="reloadBtn" style="vertical-align: text-bottom;">
<span id="reloadBtnVisDefault">{% fa6_icon "redo" "fas" %}</span>
</button>
{% trans "Scheduling for" %} {{event}}
</h2>
<h5 class="d-inline ml-2">
<a href="{% url 'admin:event_status' event.slug %}">{% trans "Event Status" %} {% fa6_icon "level-up-alt" "fas" %}</a>
</h5>
</div>
</div>
<div class="row content">
<div class="col-md-8 col-lg-9 col-xl-10">
<div id="planCalendar"></div>
</div>
<div class="col-md-4 col-lg-3 col-xl-2" id="sidebar">
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="tab" href="#unscheduled-slots">{% trans "Unscheduled" %}</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="tab" href="#violations"><span id="violationCountBadge" class="badge bg-success">0</span> {% trans "Violation(s)" %}</a>
</li>
</ul>
<div id="sidebarContent" class="tab-content">
<div class="tab-pane fade show active" id="unscheduled-slots" style="height: 80vh;overflow-y: scroll;">
{% regroup slots_unscheduled by ak.track as slots_unscheduled_by_track_list %}
{% for track_slots in slots_unscheduled_by_track_list %}
{% if track_slots.grouper %}
<h5 class="mt-2">{{ track_slots.grouper }}</h5>
{% endif %}
{% for slot in track_slots.list %}
<div class="unscheduled-slot badge" style='background-color: {% with slot.ak.category.color as color %} {% if color %}{{ color }}{% else %}#000000;{% endif %}{% endwith %}'
{% with slot.ak.details as details %}
data-event='{ "title": "{{ slot.ak.short_name }}", "duration": {"hours": "{{ slot.duration|unlocalize }}"}, "constraint": "roomAvailable", "description": "{{ details | escapejs }}", "slotID": "{{ slot.pk }}", "backgroundColor": "{{ slot.ak.category.color }}", "url": "{% url "admin:AKModel_akslot_change" slot.pk %}"}' data-details="{{ details }}">{{ slot.ak.short_name }}
({{ slot.duration }} h)<br>{{ slot.ak.owners_list }}
{% endwith %}
</div>
{% endfor %}
{% endfor %}
</div>
<div class="tab-pane fade" id="violations">
<table class="table mt-4 mb-4">
<thead>
<tr>
<th>{% trans "Level" %}</th>
<th>{% trans "Problem" %}</th>
</tr>
</thead>
<tbody id="violationsTableBody">
<tr class="text-muted">
<td colspan="2" class="text-center">
{% trans "No violations" %}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row footer">
<!-- Currently not used -->
</div>
</div>
</body>
</html>
{% endlocalize %}