dockerisation

This commit is contained in:
2023-12-22 13:53:10 +01:00
parent dfb8eb6236
commit cda63eddba
3260 changed files with 14063 additions and 154683 deletions

438
templates/Scrum/edit.html.twig Executable file
View File

@ -0,0 +1,438 @@
{% extends 'base.html.twig' %}
{% block body %}
{{ form_start(form) }}
<h1 class="page-header">
{% if mode=="update" %}
Modification SCRUM
{% elseif mode=="submit" %}
Création SCRUM
{% endif %}
</h1>
{{ form_widget(form.submit) }}
<a class="btn btn-secondary" href={{ path('app_scrum') }}>Annuler</a>
{% if mode=="update" %}
<a href="{{ path('app_scrum_delete',{'id':scrum.id}) }}"
class="btn btn-danger float-right"
data-method="delete"
data-confirm="Êtes-vous sûr de vouloir supprimer cet entregistrement ?">
Supprimer
</a>
{% endif %}
<br><br>
{% if app.session.flashbag.has('error') %}
<div class='alert alert-danger' style='margin: 5px 0px'>
<strong>Erreur</strong><br>
{% for flashMessage in app.session.flashbag.get('error') %}
{{ flashMessage }}<br>
{% endfor %}
</div>
{% endif %}
{% if app.session.flashbag.has('notice') %}
<div class='alert alert-info' style='margin: 5px 0px'>
<strong>Information</strong><br>
{% for flashMessage in app.session.flashbag.get('notice') %}
{{ flashMessage }}<br>
{% endfor %}
</div>
{% endif %}
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<i class="fa fa-pencil-alt fa-fw"></i> Informations
</div>
<div class="card-body">
{{ form_row(form.category) }}
{{ form_row(form.name) }}
{{ form_row(form.giteaid) }}
{{ form_row(form.users) }}
</div>
</div>
</div>
{% if mode=="update" %}
<div class="col-md-6">
<div class="card mb-3">
<div class="card-header">
<i class="fa fa-pencil-alt fa-fw"></i> Equipes
<button id="addteam" type="button" class="btn float-right fa fa-plus"></button>
</div>
<div id="scrumteams" class="card-body">
<ol id="scrumteams" class="list-group list-group-numbered">
</ol>
</div>
</div>
<div class="card mb-3">
<div class="card-header">
<i class="fa fa-pencil-alt fa-fw"></i> Types
<button id="addtype" type="button" class="btn float-right fa fa-plus"></button>
</div>
<div id="scrumtypes" class="card-body">
<ol id="scrumtypes" class="list-group list-group-numbered">
</ol>
</div>
</div>
<div class="card mb-3">
<div class="card-header">
<i class="fa fa-pencil-alt fa-fw"></i> Priorités
<button id="addpriority" type="button" class="btn float-right fa fa-plus"></button>
</div>
<div id="scrumprioritys" class="card-body">
<ol id="scrumprioritys" class="list-group list-group-numbered">
</ol>
</div>
</div>
<div class="card">
<div class="card-header">
<i class="fa fa-pencil-alt fa-fw"></i> Colonnes
<button id="addcolumn" type="button" class="btn float-right fa fa-plus"></button>
</div>
<div id="scrumcolumns" class="card-body">
<ol id="scrumcolumns" class="list-group list-group-numbered">
</ol>
</div>
</div>
</div>
{% endif %}
</div>
{{ form_end(form) }}
<div id="mymodalcolumn" class="modal" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title"></h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<iframe frameborder=0 width="100%" height="600px"></iframe>
</div>
</div>
</div>
</div>
<div id="mymodalteam" class="modal" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title"></h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<iframe frameborder=0 width="100%" height="600px"></iframe>
</div>
</div>
</div>
</div>
<div id="mymodalpriority" class="modal" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title"></h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<iframe frameborder=0 width="100%" height="600px"></iframe>
</div>
</div>
</div>
</div>
<div id="mymodaltype" class="modal" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title"></h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<iframe frameborder=0 width="100%" height="600px"></iframe>
</div>
</div>
</div>
</div>
{% endblock %}
{% block localjavascript %}
$(document).ready(function() {
$("#scrum_giteaid").select2();
$("#scrum_name").focus();
{% if mode=="update" %}
loadscrumcolumns();
loadscrumteams();
loadscrumprioritys();
loadscrumtypes();
{%endif%}
});
{% if mode=="update" %}
$("#addcolumn").click(function() {
ModalLoad('mymodalcolumn','Ajouter une colonne','{{path('app_scrumcolumn_submit',{scrumid:scrum.id})}}');
});
$('#scrumcolumns').on('click', '.modcolumn', function(event) {
url="{{path('app_scrumcolumn_update',{id:'xxx'})}}";
url=url.replace("xxx",$(this).data("id"));
ModalLoad('mymodalcolumn','Modifier une colonne',url);
});
$('#mymodalcolumn').on('hidden.bs.modal', function () {
loadscrumcolumns();
});
function loadscrumcolumns() {
$("#scrumcolumns").empty();
$.ajax({
method: "POST",
url: "{{path("app_scrumcolumn_select",{scrumid:scrum.id})}}",
success: function(datas, dataType)
{
jQuery.each(datas, function(i, wid) {
html ='<li data-id="'+wid.id+'" class="list-group-item d-flex justify-content-between">';
html+='<div>';
html+='<div class="mr-3 p-2 d-inline-block"><i class="fas fa-arrows-alt-v fa-2x"></i></div>';
html+='<div class="d-inline-block">';
html+=wid.name;
html+='</div>';
html+='</div>';
html+='<button type="button" data-id="'+wid.id+'" class="modcolumn btn float-right fa fa-file"></button>';
html+='</li>';
$("#scrumcolumns").append(html);
});
$( "#scrumcolumns" ).sortable({
axis: "y",
handle: ".fa-arrows-alt-v",
update: function( event, ui ) {
lstordered="";
$( "#scrumcolumns li" ).each(function( index ) {
if(index==0) lstordered=$(this).data("id");
else lstordered=lstordered+","+$(this).data("id");
});
$.ajax({
method: "POST",
url: "{{path("app_scrumcolumn_order",{scrumid:scrum.id})}}",
data: {
lstordered:lstordered
}
});
}
});
},
});
}
$("#addteam").click(function() {
ModalLoad('mymodalteam','Ajouter une colonne','{{path('app_scrumteam_submit',{scrumid:scrum.id})}}');
});
$('#scrumteams').on('click', '.modteam', function(event) {
url="{{path('app_scrumteam_update',{id:'xxx'})}}";
url=url.replace("xxx",$(this).data("id"));
ModalLoad('mymodalteam','Modifier une team',url);
});
$('#mymodalteam').on('hidden.bs.modal', function () {
loadscrumteams();
});
function loadscrumteams() {
$("#scrumteams").empty();
$.ajax({
method: "POST",
url: "{{path("app_scrumteam_select",{scrumid:scrum.id})}}",
success: function(datas, dataType)
{
jQuery.each(datas, function(i, wid) {
html ='<li data-id="'+wid.id+'" class="list-group-item d-flex justify-content-between">';
html+='<div>';
html+='<div class="mr-3 p-2 d-inline-block"><i class="fas fa-arrows-alt-v fa-2x"></i></div>';
html+='<div class="d-inline-block">';
html+=wid.name;
html+='</div>';
html+='</div>';
html+='<button type="button" data-id="'+wid.id+'" class="modteam btn float-right fa fa-file"></button>';
html+='</li>';
$("#scrumteams").append(html);
});
$( "#scrumteams" ).sortable({
axis: "y",
handle: ".fa-arrows-alt-v",
update: function( event, ui ) {
lstordered="";
$( "#scrumteams li" ).each(function( index ) {
if(index==0) lstordered=$(this).data("id");
else lstordered=lstordered+","+$(this).data("id");
});
$.ajax({
method: "POST",
url: "{{path("app_scrumteam_order",{scrumid:scrum.id})}}",
data: {
lstordered:lstordered
}
});
}
});
},
});
}
$("#addpriority").click(function() {
ModalLoad('mymodalpriority','Ajouter une colonne','{{path('app_scrumpriority_submit',{scrumid:scrum.id})}}');
});
$('#scrumprioritys').on('click', '.modpriority', function(event) {
url="{{path('app_scrumpriority_update',{id:'xxx'})}}";
url=url.replace("xxx",$(this).data("id"));
ModalLoad('mymodalpriority','Modifier une priority',url);
});
$('#mymodalpriority').on('hidden.bs.modal', function () {
loadscrumprioritys();
});
function loadscrumprioritys() {
$("#scrumprioritys").empty();
$.ajax({
method: "POST",
url: "{{path("app_scrumpriority_select",{scrumid:scrum.id})}}",
success: function(datas, dataType)
{
jQuery.each(datas, function(i, wid) {
html ='<li data-id="'+wid.id+'" class="list-group-item d-flex justify-content-between">';
html+='<div>';
html+='<div class="mr-3 p-2 d-inline-block"><i class="fas fa-arrows-alt-v fa-2x"></i></div>';
html+='<div class="d-inline-block">';
html+=wid.name;
html+='</div>';
html+='</div>';
html+='<button type="button" data-id="'+wid.id+'" class="modpriority btn float-right fa fa-file"></button>';
html+='</li>';
$("#scrumprioritys").append(html);
});
$( "#scrumprioritys" ).sortable({
axis: "y",
handle: ".fa-arrows-alt-v",
update: function( event, ui ) {
lstordered="";
$( "#scrumprioritys li" ).each(function( index ) {
if(index==0) lstordered=$(this).data("id");
else lstordered=lstordered+","+$(this).data("id");
});
$.ajax({
method: "POST",
url: "{{path("app_scrumpriority_order",{scrumid:scrum.id})}}",
data: {
lstordered:lstordered
}
});
}
});
},
});
}
$("#addtype").click(function() {
ModalLoad('mymodaltype','Ajouter une colonne','{{path('app_scrumtype_submit',{scrumid:scrum.id})}}');
});
$('#scrumtypes').on('click', '.modtype', function(event) {
url="{{path('app_scrumtype_update',{id:'xxx'})}}";
url=url.replace("xxx",$(this).data("id"));
ModalLoad('mymodaltype','Modifier un type',url);
});
$('#mymodaltype').on('hidden.bs.modal', function () {
loadscrumtypes();
});
function loadscrumtypes() {
$("#scrumtypes").empty();
$.ajax({
method: "POST",
url: "{{path("app_scrumtype_select",{scrumid:scrum.id})}}",
success: function(datas, dataType)
{
jQuery.each(datas, function(i, wid) {
html ='<li data-id="'+wid.id+'" class="list-group-item d-flex justify-content-between">';
html+='<div>';
html+='<div class="mr-3 p-2 d-inline-block"><i class="fas fa-arrows-alt-v fa-2x"></i></div>';
html+='<div class="d-inline-block">';
html+=wid.name;
html+='</div>';
html+='</div>';
html+='<button type="button" data-id="'+wid.id+'" class="modtype btn float-right fa fa-file"></button>';
html+='</li>';
$("#scrumtypes").append(html);
});
$( "#scrumtypes" ).sortable({
axis: "y",
handle: ".fa-arrows-alt-v",
update: function( event, ui ) {
lstordered="";
$( "#scrumtypes li" ).each(function( index ) {
if(index==0) lstordered=$(this).data("id");
else lstordered=lstordered+","+$(this).data("id");
});
$.ajax({
method: "POST",
url: "{{path("app_scrumtype_order",{scrumid:scrum.id})}}",
data: {
lstordered:lstordered
}
});
}
});
},
});
}
{% endif %}
{% endblock %}

View File

@ -0,0 +1,175 @@
{% extends "base.html.twig" %}
{% block localstyle %}
{% endblock %}
{% block body %}
<div class="d-flex justify-content-start mt-3">
<div class="pr-2">
{% if app.user and is_granted('ROLE_MASTER') %}
<a class="btn btn-success btn-sm mt-2" href={{ path('app_scrum_submit') }}><i class="fa fa-plus mr-2"></i>Ajouter un Scrum</a>
{% endif %}
</div>
<div style="width:300px" class="pr-2">
<label class="control-label">Filtre CATEGORIES</label>
<select id="filtercategorys" multiple="multiple" class="form-control">
{% for giteacategory in giteacategorys %}
<option value="{{giteacategory}}">{{giteacategory}}</option>
{% endfor %}
</select>
</div>
<div style="width:300px" class="pr-2">
<label class="control-label">Filtre PROJETS</label>
<select id="filterrepos" multiple="multiple" class="form-control">
{% for gitearepo in gitearepos %}
<option value="{{gitearepo.id}}">{{gitearepo.full_name}}</option>
{% endfor %}
</select>
</div>
</div>
<div class="mt-4" style="zoom:80%">
<table class="table table-striped table-bordered table-hover" id="dataTables" style="width:100%">
<thead>
<tr>
<th class="no-sort"></th>
<th style="width:100px">Catégorie</th>
<th style="width:200px">Projet</th>
<th>Description</th>
<th style="width:100px" class="no-string">Tickets Ouvert</th>
</tr>
</thead>
<tbody>
{% for scrum in scrums %}
<tr data-category="{{scrum.category}}" data-repo="{{scrum.giteaid}}">
<td>
<a href="{{path('app_scrum_view',{id:scrum.id})}}"><i class="fas fa-columns fa-2x mr-1"></i></a>
<a href="{{path('app_issuescrum',{id:scrum.id})}}"><i class="fas fa-ticket-alt fa-2x mr-1"></i></a>
<a href="{{path('app_scrum_stat',{id:scrum.id})}}"><i class="fas fa-chart-area fa-2x mr-1"></i></a>
{% if is_granted('ROLE_ADMIN') or is_granted('ROLE_MODO') or is_granted('ROLE_MASTER') %}
<a href="{{path('app_scrum_update',{id:scrum.id})}}"><i class="fas fa-file fa-2x"></i></a>
{% endif %}
</td>
<td>
{{scrum.category}}
</td>
<td>
{{scrum.name}}
</td>
<td>
{{scrum.giteajson.description}}
</td>
<td>
{{scrum.giteajson.open_issues_count}}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
{% block localjavascript %}
var table;
function showhide() {
categoryfilters=$("#filtercategorys").val();
repofilters=$("#filterrepos").val();
$.fn.dataTable.ext.search.push(function(settings, data, dataIndex, rowObj, counter) {
el=table.row(dataIndex).nodes().to$();
category = $(el).data('category');
repo = $(el).data('repo');
toreturn=true;
if(categoryfilters.length!==0 && jQuery.inArray(category.toString(), categoryfilters )<0) {
toreturn=false;
}
if(repofilters.length!==0 && jQuery.inArray(repo.toString(), repofilters )<0) {
toreturn=false;
}
return toreturn;
});
table.draw();
//$.fn.dataTable.ext.search.pop();
}
function filtercategorys() {
$.ajax({
method: "POST",
url: "{{ path('app_user_preference') }}",
data: {
key:'filtercategorys',
id:0,
value: $("#filtercategorys").val()
}
});
showhide();
}
function filterrepos() {
$.ajax({
method: "POST",
url: "{{ path('app_user_preference') }}",
data: {
key:'filterrepos',
id:0,
value: $("#filterrepos").val()
}
});
showhide();
}
$(document).ready(function() {
$('#filtercategorys').select2();
{% if filtercategorys %}
{% for category in filtercategorys %}
$("#filtercategorys").val($("#filtercategorys").val().concat("{{category}}"));
{%endfor%}
$('#filtercategorys').trigger('change');
{% endif %}
$('#filtercategorys').on("select2:select", function(e) {
filtercategorys();
});
$('#filtercategorys').on("select2:unselect", function(e) {
filtercategorys();
});
$('#filterrepos').select2();
{% if filterrepos %}
{% for repo in filterrepos %}
$("#filterrepos").val($("#filterrepos").val().concat("{{repo}}"));
{%endfor%}
$('#filterrepos').trigger('change');
{% endif %}
$('#filterrepos').on("select2:select", function(e) {
filterrepos();
});
$('#filterrepos').on("select2:unselect", function(e) {
filterrepos();
});
table = $('#dataTables').DataTable({
columnDefs: [ { "targets": "no-sort", "orderable": false }, { "targets": "no-string", "type" : "num" } ],
responsive: true,
iDisplayLength: 100,
order: [[ 1, "asc" ]],
});
showhide();
});
{% endblock %}

View File

@ -0,0 +1,284 @@
{% extends "base.html.twig" %}
{% block localstyle %}
#main {
padding-left:0px;
margin-bottom:0px;
}
#mycontent { display:none; }
.flot-chart {
display: block;
height: 450px;
}
.flot-chart-content {
width: 400px;
height: 250px;
}
.flot-chart-subcontent {
height: 200px;
width: 40%;
}
{% endblock %}
{% block body %}
<div class="d-flex">
<div id="filters" class="d-flex flex-column pl-2 pr-2 " style="width:350px; background-color:var(--colorbgbodydark);min-height:1500px;">
<div style="width:100%" class="mt-3">
<label class="control-label" style="color:var(--colorftbodydark)">Filtre JALONS</label>
<select id="filtermilestones" multiple="multiple" class="form-control">
{% for giteamilestone in giteamilestones %}
<option value="{{giteamilestone.id}}">{{giteamilestone.title}}</option>
{% endfor %}
<option value="-100">Aucun</option>
</select>
</div>
<div style="width:100%" class="mt-3">
<label class="control-label" style="color:var(--colorftbodydark)">Filtre EQUIPES</label>
<select id="filterteams" multiple="multiple" class="form-control">
{% for team in scrum.scrumteams %}
<option value="{{team.giteaid}}">{{team.name}}</option>
{% endfor %}
</select>
</div>
</div>
<div class="pl-3" style="width:100%;">
<div class="mt-4 mb-3" style="zoom:80%">
<button class="btn btn-success" onClick="showFilters()"><i class="fas fa-filter"></i></button>
<a class="btn btn-success" href="{{path('app_scrum_view',{id:scrum.id})}}"><i class="fas fa-columns"></i></a>
<a class="btn btn-success" href="{{path('app_issuescrum',{id:scrum.id})}}"><i class="fas fa-ticket-alt"></i></a>
<span id="textfilters"></span>
</div>
<h1>{{ scrum.name }}</h1>
<div class="d-flex flex-column mt-4">
{% for milestone in tbstat %}
<div class="flot-chart mr-4" data-milestone="{{ milestone.id }}">
<h4>{{milestone.name}}</h4>
<div class="mt-4">
<div id="floatdonut{{ milestone.id }}" class="flot-chart-content" style="float:left"></div>
<div class="mt-3 pb-3" style="width:100%">
<div class="d-flex justify-content-between mt-4">
{% for column in milestone.stat %}
<div style="width:250px">
<div style="background-color:{{column.color}}; padding:3px;margin-bottom:3px;">{{ column.label }} = {{ column.total }}</div>
<small>
<div style="line-height:15px; width:100%">
{% for label in column.labels %}
<li >{{label.label}} = {{label.total}}</li>
{% endfor %}
</div>
</small>
</div>
{%endfor%}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endblock %}
{% block localjavascript %}
function showFilters() {
if($("#filters").hasClass("d-flex")) {
toshow=0;
$("#filters").addClass("d-none");
$("#filters").removeClass("d-flex");
}
else {
toshow=1;
$("#filters").addClass("d-flex");
$("#filters").removeClass("d-none");
}
$.ajax({
method: "POST",
url: "{{ path('app_user_preference') }}",
data: {
key:'showfilters',
id:{{scrum.id}},
value: toshow
}
});
}
$(document).ready(function() {
// Apply Filter
function showhide() {
if($("#filtermilestones").val().length !== 0) {
$("[data-milestone]").hide();
$.each($("#filtermilestones").val(), function( index, value ) {
$("[data-milestone="+value+"]").show();
});
}
else $("[data-milestone]").show();
textfilters="";
if($("#filtermilestones").val().length!==0) {
data = $("#filtermilestones").select2('data');
textfilters=textfilters+"&nbsp;&nbsp;&nbsp;&nbsp;<b>JALONS</b> =";
$.each($("#filtermilestones").val(), function( index, value ) {
if(index>0)textfilters=textfilters+" &";
textfilters=textfilters+" "+data[index].text;
});
}
if($("#filterteams").val().length!==0) {
data = $("#filterteams").select2('data');
textfilters=textfilters+"&nbsp;&nbsp;&nbsp;&nbsp;<b>EQUIPES</b> =";
$.each($("#filterteams").val(), function( index, value ) {
if(index>0)textfilters=textfilters+" &";
textfilters=textfilters+" "+data[index].text;
});
}
$("#textfilters").html(textfilters);
}
// Filter Milestones
function filtermilestones() {
$.ajax({
method: "POST",
url: "{{ path('app_user_preference') }}",
data: {
key:'filtermilestones',
id:{{scrum.id}},
value: $("#filtermilestones").val()
}
});
showhide();
}
$('#filtermilestones').select2();
{% if filtermilestones %}
{% for milestone in filtermilestones %}
$("#filtermilestones").val($("#filtermilestones").val().concat("{{milestone}}"));
{%endfor%}
$('#filtermilestones').trigger('change');
{% endif %}
$('#filtermilestones').on("select2:select", function(e) {
filtermilestones();
});
$('#filtermilestones').on("select2:unselect", function(e) {
filtermilestones();
});
// Filter Teams
function filterteams() {
$.ajax({
method: "POST",
url: "{{ path('app_user_preference') }}",
data: {
key:'filterteams',
id:{{scrum.id}},
value: $("#filterteams").val()
}
});
location.reload();
}
$('#filterteams').select2();
{% if filterteams %}
{% for team in filterteams %}
$("#filterteams").val($("#filterteams").val().concat("{{team}}"));
{%endfor%}
$('#filterteams').trigger('change');
{% endif %}
$('#filterteams').on("select2:select", function(e) {
filterteams();
});
$('#filterteams').on("select2:unselect", function(e) {
filterteams();
});
{% for milestone in tbstat %}
var data = [
{% for data in milestone.stat %}
{
label: "{{ data.label}}",
data: {{ data.total }},
color: "{{ data.color }}",
},
{% endfor %}
];
var plotObj = $.plot($("#floatdonut{{ milestone.id }}"), data, {
series: {
pie: {
show: true,
radius: 1,
label: {
show: true,
radius: 1,
threshold: 0.1,
background: {
opacity: 0.5,
color: '#cdcdcd',
},
formatter: function(label, series) {
return '<span style="color:#000; padding:3px;">' + label + '</span>';
}
}
}
},
legend: {
show: false,
},
});
{% for data in milestone.stat %}
{% for data in milestone.stat %}
var data = [
{% for label in data.labels %}
{
label: "{{ label.label}}",
data: {{ label.total }},
color: "{{ label.color }}",
},
{% endfor %}
];
var plotObj = $.plot($("#floatsubdonut{{ milestone.id }}-{{ data.id }}"), data, {
series: {
pie: {
show: true,
label: {
show: false,
radius: 3/4,
threshold: 0.1,
background: {
opacity: 0.5,
color: '#cdcdcd',
},
}
}
},
legend: {
show: false,
},
});
{% endfor %}
{% endfor %}
{% endfor %}
{% if not showfilters %}
$("#filters").addClass("d-none");
$("#filters").removeClass("d-flex");
{% endif %}
// Appliy filters
showhide();
$("#mycontent").show();
});
{% endblock %}

View File

@ -0,0 +1,820 @@
{% extends "base.html.twig" %}
{% block localstyle %}
html { height:100%}
body { height:100%; background-color:#efefef;}
#main {
padding-left:0px;
margin-bottom:0px;
}
h3 { font-size:14px; margin-top:15px;}
#mycontent { width: 5000px; display:none; }
.btn-link {
cursor:pointer;
}
.control-label {
margin-bottom:0px;
}
.tag {
border-radius: 5px;
padding: 8px 8px;
margin-bottom: 5px;
display: inline-block;
min-width: 35px;
text-align: center;
color: #ffffff !important;
zoom: 80%;
}
.tag i {
margin-right:5px;
}
.assignee {
width:30px;
margin: 5px 5px 0px 0px;
}
.state-closed {
background-color: #cdcdcd !important;
}
{% endblock %}
{% block body %}
<div class="d-flex">
<div id="filters" class="d-flex flex-column pl-2 pr-2 " style="width:350px; background-color:var(--colorbgbodydark);min-height:1500px;">
<div style="width:100%" class="mt-3">
<label class="control-label" style="color:var(--colorftbodydark)">Filtre JALONS</label>
<select id="filtermilestones" multiple="multiple" class="form-control">
{% for giteamilestone in giteamilestones %}
<option value="{{giteamilestone.id}}">{{giteamilestone.title}}</option>
{% endfor %}
<option value="-100">Aucun</option>
</select>
</div>
<div style="width:100%" class="mt-3">
<label class="control-label" style="color:var(--colorftbodydark)">Filtre TYPES</label>
<select id="filtertypes" multiple="multiple" class="form-control">
{% for type in scrum.scrumtypes %}
<option value="{{type.giteaid}}">{{type.name}}</option>
{% endfor %}
</select>
</div>
<div style="width:100%" class="mt-3">
<label class="control-label" style="color:var(--colorftbodydark)">Filtre EQUIPES</label>
<select id="filterteams" multiple="multiple" class="form-control">
{% for team in scrum.scrumteams %}
<option value="{{team.giteaid}}">{{team.name}}</option>
{% endfor %}
</select>
</div>
<div style="width:100%" class="mt-3">
<label class="control-label" style="color:var(--colorftbodydark)">Filtre PRIORITES</label>
<select id="filterprioritys" multiple="multiple" class="form-control">
{% for priority in scrum.scrumprioritys %}
<option value="{{priority.giteaid}}">{{priority.name}}</option>
{% endfor %}
</select>
</div>
<div style="width:100%" class="mt-3">
<label class="control-label" style="color:var(--colorftbodydark)">Filtre ETIQUETTES</label>
<select id="filterlabels" multiple="multiple" class="form-control">
{% for gitealabel in gitealabels %}
<option value="{{gitealabel.id}}">{{gitealabel.name}}</option>
{% endfor %}
</select>
</div>
<div style="width:100%" class="mt-3">
<label class="control-label" style="color:var(--colorftbodydark)">Filtre AFFECTATIONS</label>
<select id="filterassignees" multiple="multiple" class="form-control">
{% for giteaassignee in giteaassignees %}
<option value="{{giteaassignee.id}}">{{giteaassignee.login}}</option>
{% endfor %}
</select>
</div>
<div style="width:100%" class="mt-3">
<label class="control-label" style="color:var(--colorftbodydark)">Filtre EXCLUSIONS</label>
<select id="filterexcludes" multiple="multiple" class="form-control">
{% for gitealabel in gitealabels %}
<option value="{{gitealabel.id}}">{{gitealabel.name}}</option>
{% endfor %}
</select>
</div>
<div style="color:var(--colorftbodydark);zoom:75%;" class="mt-5">
{% for giteamilestone in giteamilestones %}
{{giteamilestone.title}} = <span id="total{{giteamilestone.id}}" class="totalweight">0</span><br>
{% endfor %}
Aucun = <span id="total-100" class="totalweight">0</span><br>
</div>
</div>
<div class="pl-3" style="width:100%;">
<div id="haveupdate" style="display:none" class="alert alert-danger pr-2 mt-3">
Votre projet a été mise à jour par un tier. Souhaitez-vous raffraichir votre projet ?<br>
<a href="{{path('app_scrum_view',{id:scrum.id})}}" class="btn btn-success mt-3">Rafraichir</a>
</div>
<div class="mt-4" style="zoom:80%">
<button class="btn btn-success" onClick="showFilters()"><i class="fas fa-filter"></i></button>
<a class="btn btn-success" href="{{path('app_issuescrum',{id:scrum.id})}}"><i class="fas fa-ticket-alt"></i></a>
<a class="btn btn-success" href="{{path('app_scrum_stat',{id:scrum.id})}}"><i class="fas fa-chart-area"></i></a>
<a href="{{giteaUrl}}/{{scrum.giteajson.owner.login}}/{{scrum.giteajson.name}}/issues/new" class="btn btn-success" target="_blank">Nouveau Ticket</a>
<span id="textfilters"></span>
</div>
{% for column in scrum.scrumcolumns %}
{% set idmiletone="-100" %}
{% set tbidmiletone=[] %}
{% set haveissues=false %}
<div class="card d-inline-block mt-3 align-top mb-5" data-column="{{column.giteaid}}" style="width: 18rem;">
<div class="card-header">{{ column.name }}</div>
<div class="card-body p-1">
{% for issue in column.scrumissues %}
{% set tosee=false %}
{% if app.session.get('viewclosed')=="true" and issue.giteastate=="closed" %}
{% set tosee=true %}
{% endif %}
{% if issue.giteastate!="closed" %}
{% set tosee=true %}
{% endif %}
{% if tosee %}
{% set haveissues=true %}
{% if idmiletone!=issue.giteamilestone %}
{% if idmiletone!=-100 %}</ul>{% endif %}
{% if issue.giteamilestone is empty %}
{% set tbidmiletone = tbidmiletone|merge([-100]) %}
{% set idmilestone = -100 %}
{% set namemilestone = "Aucun" %}
{% else %}
{% set tbidmiletone = tbidmiletone|merge([issue.giteamilestone]) %}
{% set idmilestone = issue.giteamilestone %}
{% set namemilestone = issue.giteajson.milestone.title %}
{% endif %}
<h3 data-milestone="{{idmilestone}}">JALON = {{namemilestone}}</h3>
<ul class="scrumcolumn list-group" style="min-height:50px" data-column="{{column.giteaid}}" data-milestone="{{idmilestone}}">
{% set idmiletone=issue.giteamilestone %}
{% endif %}
{% set datalabels="" %}
{% set datateams="" %}
{% set datatypes="" %}
{% set dataprioritys="datapriority"~giteaprioritys|last %}
{% for label in issue.giteajson.labels %}
{% if label.id not in giteacolumns and label.id in giteateams %}
{% set datateams=datateams~"datateam"~label.id~" " %}
{% endif %}
{% if label.id not in giteacolumns and label.id in giteatypes %}
{% set datatypes=datatypes~"datatype"~label.id~" " %}
{% endif %}
{% if label.id not in giteacolumns and label.id in giteaprioritys %}
{% set dataprioritys="datapriority"~label.id~" " %}
{% endif %}
{% if label.id not in giteacolumns and label.id not in giteateams and label.id not in giteatypes and label.id not in giteaprioritys %}
{% set datalabels=datalabels~"datalabel"~label.id~" " %}
{% endif %}
{% endfor %}
{% set dataassignees="" %}
{% for assignee in issue.giteajson.assignees %}
{% set dataassignees=dataassignees~"dataassignee"~assignee.id~" " %}
{% endfor %}
<div id="issu{{ issue.id }}" data-id="{{ issue.id }}" data-issue="{{ issue.id }}" data-column="{{column.giteaid}}" data-milestone="{{idmilestone}}" class="card mb-1 issue issue-{{issue.id}} {{datateams}} {{datatypes}} {{dataprioritys}} {{datalabels}} {{dataassignees}} state-{{issue.giteastate}}">
<div class="card-footer p-1" style="line-height:10px; border-top:none;">
<div class="float-left btn btn-link p-0 m-0 fas fa-arrows-alt" style="cursor:move"></div>
<a target="_blank" class="modcolumn btn btn-link float-right fa fa-file p-0 m-0" href="{{issue.giteajson.html_url}}"></a>
</div>
<div class="card-body p-1" style="line-height:10px;">
<div>
<small>
{{issue.giteanumber}} - {{ issue.giteatitle }}<br><br>
<small>Crée le {{issue.giteajson.created_at|date("d/m/y")}}</small><small style ="float:right">Modifié le {{issue.giteajson.updated_at|date("d/m/y")}}</small>
{% if issue.giteastate=="closed" %}
<br><small>Clos le {{issue.giteajson.closed_at|date("d/m/y")}}</small>
{% endif %}
<br><br>
{% for label in issue.giteajson.labels %}
{% if label.id not in giteacolumns and label.id in giteaprioritys %}
<span class="btn-link tag" style="background-color:#{{label.color}}">
<i class="fas fa-tag"></i>
{{ label.name }}
</span>
{% endif %}
{% endfor %}
<br>
{% for label in issue.giteajson.labels %}
{% if label.id not in giteacolumns and label.id in giteatypes %}
<span class="btn-link tag" style="background-color:#{{label.color}}">
<i class="fas fa-tag"></i>
{{ label.name }}
</span>
{% endif %}
{% endfor %}
<br>
{% for label in issue.giteajson.labels %}
{% if label.id not in giteacolumns and label.id in giteateams %}
<span class="btn-link tag" style="background-color:#{{label.color}}">
<i class="fas fa-tag"></i>
{{ label.name }}
</span>
{% endif %}
{% endfor %}
<br>
{% for label in issue.giteajson.labels %}
{% if label.id not in giteacolumns and label.id not in giteateams and label.id not in giteaprioritys and label.id not in giteatypes %}
<span class="btn-link tag" style="background-color:#{{label.color}}">
<i class="fas fa-tag"></i>
{{ label.name }}
</span>
{% endif %}
{% endfor %}
<div>
{% for assignee in issue.giteajson.assignees %}
<img src="{{assignee.avatar_url}}" class="assignee" title="{{assignee.username}}">
{% endfor %}
</div>
<div id="modissu{{ issue.id }}" data-issue="{{ issue.id }}" data-giteaid="{{issue.giteanumber}}" data-giteatitle="{{ issue.giteatitle }}" type="button" class="modissu btn btn-link float-right">
<i class="fas fa-weight-hanging"></i> = <span id="issue{{issue.id}}-weight">{{ issue.weight }}</span>
</div>
</small>
</div>
</div>
</div>
{% endif %}
{% endfor %}
{% if haveissues %}
</ul>
{% endif %}
{% for giteamilestone in giteamilestones %}
{% if giteamilestone.id not in tbidmiletone %}
<h3 data-milestone="{{giteamilestone.id}}"> JALON = {{giteamilestone.title}}</h3>
<ul class="scrumcolumn list-group" style="min-height:50px" data-column="{{ column.giteaid }}" data-milestone="{{giteamilestone.id}}"></ul>
{% endif %}
{%endfor%}
{% if -100 not in tbidmiletone %}
<h3 data-milestone="-100"> JALON = Aucun</h3>
<ul class="scrumcolumn list-group" style="min-height:50px" data-column="{{ column.giteaid }}" data-milestone="-100"></ul>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
<div id="mymodalissue" class="modal" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title"></h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group ">
<label class="control-label required" for="issu_weight">
Poid
</label>
<input type="hidden" id="modal-issueid" name="modal-issueid" required="required" class=" form-control" value="">
<input type="integer" id="modal-issueweight" name="modal-issueweight" required="required" class=" form-control" value="">
</div>
<button id="issu_update" class="btn btn-success">Enregistrer</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block localjavascript %}
function showFilters() {
if($("#filters").hasClass("d-flex")) {
toshow=0;
$("#filters").addClass("d-none");
$("#filters").removeClass("d-flex");
}
else {
toshow=1;
$("#filters").addClass("d-flex");
$("#filters").removeClass("d-none");
}
$.ajax({
method: "POST",
url: "{{ path('app_user_preference') }}",
data: {
key:'showfilters',
id:{{scrum.id}},
value: toshow
}
});
}
$(document).ready(function() {
// Apply Filter
function showhide() {
if($("#filtermilestones").val().length !== 0) {
$("[data-milestone]").hide();
$.each($("#filtermilestones").val(), function( index, value ) {
$("[data-milestone="+value+"]").show();
});
}
else $("[data-milestone]").show();
$(".issue").show();
$( ".issue" ).each(function( index ) {
domissue=$(this);
toshow=true;
if(toshow&&$("#filterteams").val().length !== 0) {
toshow=false;
$.each($("#filterteams").val(), function( index, value ) {
if (domissue.hasClass("datateam"+value))
toshow=true;
});
}
if(toshow&&$("#filterprioritys").val().length !== 0) {
toshow=false;
$.each($("#filterprioritys").val(), function( index, value ) {
if (domissue.hasClass("datapriority"+value))
toshow=true;
});
}
if(toshow&&$("#filtertypes").val().length !== 0) {
toshow=false;
$.each($("#filtertypes").val(), function( index, value ) {
if (domissue.hasClass("datatype"+value))
toshow=true;
});
}
if(toshow&&$("#filterlabels").val().length !== 0) {
toshow=false;
$.each($("#filterlabels").val(), function( index, value ) {
if(domissue.hasClass("datalabel"+value))
toshow=true;
});
}
if(toshow&&$("#filterassignees").val().length !== 0) {
toshow=false;
$.each($("#filterassignees").val(), function( index, value ) {
if (domissue.hasClass("dataassignee"+value))
toshow=true;
});
}
if(toshow&&$("#filterexcludes").val().length !== 0) {
$.each($("#filterexcludes").val(), function( index, value ) {
if(domissue.hasClass("datalabel"+value))
toshow=false;
});
}
if(!toshow) domissue.hide();
});
textfilters="";
if($("#filtermilestones").val().length!==0) {
data = $("#filtermilestones").select2('data');
textfilters=textfilters+"&nbsp;&nbsp;&nbsp;&nbsp;<b>JALONS</b> =";
$.each($("#filtermilestones").val(), function( index, value ) {
if(index>0)textfilters=textfilters+" &";
textfilters=textfilters+" "+data[index].text;
});
}
if($("#filterteams").val().length!==0) {
data = $("#filterteams").select2('data');
textfilters=textfilters+"&nbsp;&nbsp;&nbsp;&nbsp;<b>EQUIPES</b> =";
$.each($("#filterteams").val(), function( index, value ) {
if(index>0)textfilters=textfilters+" &";
textfilters=textfilters+" "+data[index].text;
});
}
if($("#filterprioritys").val().length!==0) {
data = $("#filterprioritys").select2('data');
textfilters=textfilters+"&nbsp;&nbsp;&nbsp;&nbsp;<b>EQUIPES</b> =";
$.each($("#filterprioritys").val(), function( index, value ) {
if(index>0)textfilters=textfilters+" &";
textfilters=textfilters+" "+data[index].text;
});
}
if($("#filtertypes").val().length!==0) {
data = $("#filtertypes").select2('data');
textfilters=textfilters+"&nbsp;&nbsp;&nbsp;&nbsp;<b>EQUIPES</b> =";
$.each($("#filtertypes").val(), function( index, value ) {
if(index>0)textfilters=textfilters+" &";
textfilters=textfilters+" "+data[index].text;
});
}
if($("#filterlabels").val().length!==0) {
data = $("#filterlabels").select2('data');
textfilters=textfilters+"&nbsp;&nbsp;&nbsp;&nbsp;<b>ETIQUETTES</b> =";
$.each($("#filterlabels").val(), function( index, value ) {
if(index>0)textfilters=textfilters+" &";
textfilters=textfilters+" "+data[index].text;
});
}
if($("#filterassignees").val().length!==0) {
data = $("#filterassignees").select2('data');
textfilters=textfilters+"&nbsp;&nbsp;&nbsp;&nbsp;<b>AFFECTATIONS</b> =";
$.each($("#filterassignees").val(), function( index, value ) {
if(index>0)textfilters=textfilters+" &";
textfilters=textfilters+" "+data[index].text;
});
}
if($("#filterexcludes").val().length!==0) {
data = $("#filterexcludes").select2('data');
textfilters=textfilters+"&nbsp;&nbsp;&nbsp;&nbsp;<b>EXCLURE ETIQUETTES</b> =";
$.each($("#filterexcludes").val(), function( index, value ) {
if(index>0)textfilters=textfilters+" &";
textfilters=textfilters+" "+data[index].text;
});
}
$("#textfilters").html(textfilters);
}
function refreshinfo() {
$.ajax({
method: "POST",
url: "{{path("app_scrum_info",{id:scrum.id})}}",
success: function(data) {
$(".totalweight").html("0");
Object.entries(data.weights).forEach(entry => {
const [key, value] = entry;
$("#total"+key).html(value);
});
}
});
}
$(document).on('click','.modissu',function(){
$(".modal-title").html("#"+$(this).data("giteaid")+" - "+$(this).data("giteatitle"));
$("#modal-issueid").val($(this).data("issue"));
$.ajax({
method: "POST",
url: "{{path("app_scrumissue_info")}}",
data: {
id:$("#modal-issueid").val(),
},
success: function(data) {
$("#modal-issueweight").val(data.weight);
$("#mymodalissue").modal('show');
$("#modal-issueweight").focus();
},
});
});
$("#issu_update").click(function(){
$.ajax({
method: "POST",
url: "{{path("app_scrumissue_update")}}",
data: {
id:$("#modal-issueid").val(),
weight:$("#modal-issueweight").val(),
},
success: function(data) {
$("#issue"+$("#modal-issueid").val()+"-weight").html($("#modal-issueweight").val());
refreshinfo();
$("#mymodalissue").modal('hide');
},
error: function (request, status, error) {
$("#mymodalissue").modal('hide');
}
});
});
// Filter Milestones
function filtermilestones() {
$.ajax({
method: "POST",
url: "{{ path('app_user_preference') }}",
data: {
key:'filtermilestones',
id:{{scrum.id}},
value: $("#filtermilestones").val()
}
});
showhide();
}
$('#filtermilestones').select2();
{% if filtermilestones %}
{% for milestone in filtermilestones %}
$("#filtermilestones").val($("#filtermilestones").val().concat("{{milestone}}"));
{%endfor%}
$('#filtermilestones').trigger('change');
{% endif %}
$('#filtermilestones').on("select2:select", function(e) {
filtermilestones();
});
$('#filtermilestones').on("select2:unselect", function(e) {
filtermilestones();
});
// Filter Teams
function filterteams() {
$.ajax({
method: "POST",
url: "{{ path('app_user_preference') }}",
data: {
key:'filterteams',
id:{{scrum.id}},
value: $("#filterteams").val()
}
});
showhide();
}
$('#filterteams').select2();
{% if filterteams %}
{% for team in filterteams %}
$("#filterteams").val($("#filterteams").val().concat("{{team}}"));
{%endfor%}
$('#filterteams').trigger('change');
{% endif %}
$('#filterteams').on("select2:select", function(e) {
filterteams();
});
$('#filterteams').on("select2:unselect", function(e) {
filterteams();
});
// Filter Proritys
function filterprioritys() {
$.ajax({
method: "POST",
url: "{{ path('app_user_preference') }}",
data: {
key:'filterprioritys',
id:{{scrum.id}},
value: $("#filterprioritys").val()
}
});
showhide();
}
$('#filterprioritys').select2();
{% if filterprioritys %}
{% for priority in filterprioritys %}
$("#filterprioritys").val($("#filterprioritys").val().concat("{{priority}}"));
{%endfor%}
$('#filterprioritys').trigger('change');
{% endif %}
$('#filterprioritys').on("select2:select", function(e) {
filterprioritys();
});
$('#filterprioritys').on("select2:unselect", function(e) {
filterprioritys();
});
// Filter Types
function filtertypes() {
$.ajax({
method: "POST",
url: "{{ path('app_user_preference') }}",
data: {
key:'filtertypes',
id:{{scrum.id}},
value: $("#filtertypes").val()
}
});
showhide();
}
$('#filtertypes').select2();
{% if filtertypes %}
{% for type in filtertypes %}
$("#filtertypes").val($("#filtertypes").val().concat("{{type}}"));
{%endfor%}
$('#filtertypes').trigger('change');
{% endif %}
$('#filtertypes').on("select2:select", function(e) {
filtertypes();
});
$('#filtertypes').on("select2:unselect", function(e) {
filtertypes();
});
// Filter Labels
function filterlabels() {
$.ajax({
method: "POST",
url: "{{ path('app_user_preference') }}",
data: {
key:'filterlabels',
id:{{scrum.id}},
value: $("#filterlabels").val()
}
});
showhide();
}
$('#filterlabels').select2();
{% if filterlabels %}
{% for label in filterlabels %}
$("#filterlabels").val($("#filterlabels").val().concat("{{label}}"));
{%endfor%}
$('#filterlabels').trigger('change');
{% endif %}
$('#filterlabels').on("select2:select", function(e) {
filterlabels();
});
$('#filterlabels').on("select2:unselect", function(e) {
filterlabels();
});
// Filtre Assignees
function filterassignees() {
$.ajax({
method: "POST",
url: "{{ path('app_user_preference') }}",
data: {
key:'filterassignees',
id:{{scrum.id}},
value: $("#filterassignees").val()
}
});
showhide();
}
$('#filterassignees').select2();
{% if filterassignees %}
{% for assignee in filterassignees %}
$("#filterassignees").val($("#filterassignees").val().concat("{{assignee}}"));
{%endfor%}
$('#filterassignees').trigger('change');
{% endif %}
$('#filterassignees').on("select2:select", function(e) {
filterassignees();
});
$('#filterassignees').on("select2:unselect", function(e) {
filterassignees();
});
// Filter Labels
function filterexcludes() {
$.ajax({
method: "POST",
url: "{{ path('app_user_preference') }}",
data: {
key:'filterexcludes',
id:{{scrum.id}},
value: $("#filterexcludes").val()
}
});
showhide();
}
$('#filterexcludes').select2();
{% if filterexcludes %}
{% for label in filterexcludes %}
$("#filterexcludes").val($("#filterexcludes").val().concat("{{label}}"));
{%endfor%}
$('#filterexcludes').trigger('change');
{% endif %}
$('#filterexcludes').on("select2:select", function(e) {
filterexcludes();
});
$('#filterexcludes').on("select2:unselect", function(e) {
filterexcludes();
});
// Resume filtre
{% if not showfilters %}
$("#filters").addClass("d-none");
$("#filters").removeClass("d-flex");
{% endif %}
// Appliy filters
showhide();
refreshinfo();
$("#mycontent").show();
lastupdate="{{scrum.updatedate|date("Ymd H:i:s")}}";
// Sort columns
$( ".scrumcolumn" ).sortable({
handle: ".fa-arrows-alt",
connectWith: ".scrumcolumn",
cursor: "move",
update: function( event, ui ) {
id=$(ui.item).data("issue");
oldcolumn=$(ui.item).data("column");
oldmilestone=$(ui.item).data("milestone");
newcolumn=$(ui.item).parent().data("column");
newmilestone=$(ui.item).parent().data("milestone");
console.log("ID = "+id+" = Column : "+oldcolumn+">>"+newcolumn+" = Milestone : "+oldmilestone+">>"+newmilestone );
if(oldcolumn!=newcolumn||oldmilestone!=newmilestone) {
$.ajax({
method: "POST",
url: "{{path("app_scrumissue_change")}}",
data: {
id:id,
oldcolumn:oldcolumn,
oldmilestone:oldmilestone,
newcolumn:newcolumn,
newmilestone:newmilestone,
},
success: function(data) {
$(ui.item).data("column",newcolumn);
$(ui.item).data("milestone",newmilestone);
if(data) lastupdate=data;
refreshinfo();
},
error: function (request, status, error) {
$( ".scrumcolumn" ).sortable('cancel');
}
});
}
lstordered="";
$(".scrumcolumn div" ).each(function( index ) {
if($(this).data("id")) {
if(index==0) lstordered=$(this).data("id");
else lstordered=lstordered+","+$(this).data("id");
}
});
$.ajax({
method: "POST",
url: "{{path("app_scrumissue_order")}}",
data: {
id:{{scrum.id}},
lstordered:lstordered,
}
});
},
});
var intervalId = window.setInterval(function(){
console.log(lastupdate);
$.ajax({
method: "POST",
url: "{{path("app_scrumissue_ctrlchange")}}",
data: {
id:{{scrum.id}},
lastupdate:lastupdate
},
success: function(fgupdated) {
if(fgupdated) {
$("#haveupdate").show();
}
}
});
}, 60000);
});
{% endblock %}