Selaa lähdekoodia

Ajout de la page de cloture de sprint

omassot 7 vuotta sitten
vanhempi
commit
e8296b5ad4

+ 7 - 1
main/forms.py

@@ -7,7 +7,7 @@ from django.contrib.auth.forms import UserCreationForm
 from django.contrib.auth.models import User
 from martor.fields import MartorFormField
 
-from main.models import Story, Epic, Comment
+from main.models import Story, Epic, Comment, Sprint
 
 
 class RegisterForm(UserCreationForm):
@@ -61,6 +61,12 @@ class CommentForm(forms.ModelForm):
         model = Comment
         fields = ('content',)
     content = MartorFormField(label="Ajouter un commentaire")
+
+class SprintForm(forms.ModelForm):
+    class Meta:
+        model = Sprint
+        fields = ('retro',)
+    retro = MartorFormField(label="Rétrospective")
     
     
     

+ 1 - 0
main/templates/_layout.html

@@ -80,6 +80,7 @@
 			<ul class="links">
 				<li><a href="{% url 'index' %}">Accueil</a></li>
 				<li><a href="{% url 'story_index' %}">Toutes les Stories</a></li>
+				<li><a href="{% url 'sprint_end' %}">Cloture de sprint</a></li>
 				<li><a href="{% url 'reports' %}">Rapports</a></li>
 				<li><a href="{% url 'backlog_editor' %}">Modifier le Backlog</a></li>
 				<li><a href="{% url 'admin:index' %}">Administration</a></li>

+ 3 - 1
main/templates/epic_form.html

@@ -35,7 +35,9 @@
 		{% endif %}
 		</span>
 		
-		<input type="submit" value="Enregistrer">
+    	<div class="flex-row flex-end" style="margin-top: 10px;">
+    		<input type="submit" value="Enregistrer">
+    	</div>
 	</div>
 	
 	</form>

+ 105 - 0
main/templates/sprint_end.html

@@ -0,0 +1,105 @@
+{% extends '_layout.html' %}
+
+{% block title %}
+	Clotûre de sprint
+{% endblock %}
+
+
+{% block breadcrumb %}
+	<li><a href="{% url 'index' %}">Accueil</a></li>
+	<li><a>Clotûre de sprint</a></li>
+{% endblock %}
+
+{% block main %}
+	<header>
+		<div class="flex-row">
+			<h2 class="flex-extend">Clotûre du {{ sprint }}</h2>
+			<span>
+			</span>
+		</div>
+	</header>
+	
+	<h5>Clore les stories</h5>
+	<table>
+	{% for story in sprint.stories.all|dictsort:"id" %}
+		<tr data-id="{{ story.id }}">
+			<td class="btn-cell">
+				{% if story.closed %}
+				<a class="button special icon fa-check tool-btn" style="color: green !important; background: none !important;"></a>
+				{% else %}
+				<a href="" class="story_close button special icon fa-check tool-btn"></a>
+				{% endif %}
+			</td>
+			<td>{{ story.name }}</td>
+			<td width="1%">{% if story.weight %}<span>{% include 'weight_svg.html' with weight=story.weight h=20 %}</span>{% endif %}</td>
+		</tr>
+	{% endfor %}
+	</table>
+	
+	<h5>Rétrospective</h5>
+	<form action="." method="post" enctype="multipart/form-data">
+		{% csrf_token %}
+		
+		{{ form.retro }}
+		
+    	<div class="flex-row flex-end" style="margin-top: 10px;">
+    		<input type="submit" value="Enregistrer">
+    	</div>
+	</form>
+	
+	
+	<script>
+		function csrfSafeMethod(method) {
+		    // these HTTP methods do not require CSRF protection
+		    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
+		}
+		function getCookie(name) {
+		    var cookieValue = null;
+		    if (document.cookie && document.cookie !== '') {
+		        var cookies = document.cookie.split(';');
+		        for (var i = 0; i < cookies.length; i++) {
+		            var cookie = jQuery.trim(cookies[i]);
+		            // 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;
+		}
+		var csrftoken = getCookie('csrftoken');
+	
+		$(".story_close").click(function(event) {
+			event.preventDefault();
+			var story_tr = $(this).closest('tr');
+			var story_id = story_tr.data('id');
+			
+            $.ajax({
+                type: "POST",
+                url: "/stories/axclose/" + story_id + "/",
+                data: '{story_id:' + story_id + '}',
+                contentType: "application/json; charset=utf-8",
+                dataType: "json",
+                beforeSend: function(xhr, settings) {
+                    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
+                        xhr.setRequestHeader("X-CSRFToken", csrftoken);
+                    }
+                },
+                success: function (response) {
+                	story_tr.find('.btn-cell').html('<a class="button special icon fa-check tool-btn" style="color: green !important; background: none !important;"></a>');
+                },
+                failure: function (response) {
+                    alert(response.responseText);
+                },
+                error: function (response) {
+                    alert(response.responseText);
+                }
+            });
+			
+		});
+		
+	</script>
+
+
+{% endblock %}

+ 3 - 1
main/templates/story_form.html

@@ -89,7 +89,9 @@
 			{% endif %}
 			</span>
 			
-			<input type="submit" value="Enregistrer">
+	    	<div class="flex-row flex-end" style="margin-top: 10px;">
+	    		<input type="submit" value="Enregistrer">
+	    	</div>
 		</div>
 	
 	</form>

+ 2 - 0
main/urls.py

@@ -14,6 +14,7 @@ urlpatterns = [
     path('profile_update/', views.profile_update, name='profile_update'),
     path(r'change_password/', views.change_password, name='change_password'),
     path('edition/', views.backlog_editor, name='backlog_editor'),
+    path('sprintend/', views.sprint_end, name='sprint_end'),
     path('stories/', views.story_index, name='story_index'),
     path('stories/<int:story_id>', views.story_details, name='story_details'),
     path('stories/create/', views.story_create, name='story_create'),
@@ -21,6 +22,7 @@ urlpatterns = [
     path('stories/edit/<int:story_id>/', views.story_edit, name='story_edit'),
     path('stories/delete/<int:story_id>/', views.story_delete, name='story_delete'),
     path('stories/close/<int:story_id>/', views.story_close, name='story_close'),
+    path('stories/axclose/<int:story_id>/', views.story_close_ajax, name='story_close_ajax'),
     path('stories/reopen/<int:story_id>/', views.story_reopen, name='story_reopen'),
     path('epics/<int:epic_id>', views.epic_details, name='epic_details'),
     path('epics/create/', views.epic_create, name='epic_create'),

+ 27 - 2
main/views.py

@@ -7,11 +7,11 @@ from django.contrib.auth.forms import PasswordChangeForm
 from django.contrib.auth.models import User
 from django.core import serializers
 from django.core.paginator import Paginator
-from django.http.response import HttpResponse
+from django.http.response import HttpResponse, HttpResponseRedirect
 from django.shortcuts import render, get_object_or_404, redirect
 
 from main.forms import StoryForm, EpicForm, RegisterForm, ProfileForm, \
-    CommentForm
+    CommentForm, SprintForm
 from main.models import Story, Epic, Sprint, Comment
 
 
@@ -70,6 +70,18 @@ def backlog_editor(request):
     
     return render(request, 'backlog_editor.html', {'epics': epics, 'closed': closed})
 
+def sprint_end(request):
+    
+    current_sprint = Sprint.current()
+    
+    if request.method == 'POST':
+        current_sprint.retro = request.POST["retro"]
+        current_sprint.save()
+        return HttpResponseRedirect('')
+    
+    form = SprintForm(instance=current_sprint)
+    return render(request, 'sprint_end.html', {'sprint': current_sprint, 'form': form})
+
 @login_required
 def story_index(request):
     
@@ -152,6 +164,19 @@ def story_close(request, story_id):
     story.save()
     return render(request, 'epic_details.html', {'epic': story.epic})
 
+def story_close_ajax(_, story_id):
+    story = get_object_or_404(Story, id=story_id)
+    story.closed = True
+    story.save()
+    return HttpResponse(story.to_json())
+
+@login_required
+def story_close_from_sprintend(_, story_id):
+    story = get_object_or_404(Story, id=story_id)
+    story.closed = True
+    story.save()
+    return redirect("sprint_end")
+
 @login_required
 def story_reopen(request, story_id):
     story = get_object_or_404(Story, id=story_id)