feat(project_management): Add Project State to the UI
ref: #294 #295 #300
This commit is contained in:
87
app/project_management/forms/project_state.py
Normal file
87
app/project_management/forms/project_state.py
Normal file
@ -0,0 +1,87 @@
|
||||
from django import forms
|
||||
from django.urls import reverse
|
||||
from django.db.models import Q
|
||||
|
||||
from app import settings
|
||||
|
||||
from core.forms.common import CommonModelForm
|
||||
from core.templatetags.markdown import to_duration
|
||||
|
||||
from project_management.models.project_states import ProjectState
|
||||
|
||||
|
||||
|
||||
class ProjectStateForm(CommonModelForm):
|
||||
|
||||
class Meta:
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'organization',
|
||||
'name',
|
||||
'model_notes',
|
||||
'runbook'
|
||||
]
|
||||
|
||||
model = ProjectState
|
||||
|
||||
|
||||
# def __init__(self, *args, **kwargs):
|
||||
# super().__init__(*args, **kwargs)
|
||||
|
||||
# # self.fields['description'].widget.attrs = {'style': "height: 800px; width: 1000px"}
|
||||
|
||||
|
||||
class DetailForm(ProjectStateForm):
|
||||
|
||||
|
||||
tabs: dict = {
|
||||
"details": {
|
||||
"name": "Details",
|
||||
"slug": "details",
|
||||
"sections": [
|
||||
{
|
||||
"layout": "double",
|
||||
"left": [
|
||||
'name',
|
||||
'runbook',
|
||||
'organization',
|
||||
'c_created',
|
||||
'c_modified',
|
||||
],
|
||||
"right": [
|
||||
'model_notes',
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
"notes": {
|
||||
"name": "Notes",
|
||||
"slug": "notes",
|
||||
"sections": []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.fields['c_created'] = forms.DateTimeField(
|
||||
label = 'Created',
|
||||
input_formats=settings.DATETIME_FORMAT,
|
||||
disabled = True,
|
||||
initial = self.instance.created,
|
||||
)
|
||||
|
||||
self.fields['c_modified'] = forms.DateTimeField(
|
||||
label = 'Modified',
|
||||
input_formats=settings.DATETIME_FORMAT,
|
||||
disabled = True,
|
||||
initial = self.instance.modified,
|
||||
)
|
||||
|
||||
self.tabs['details'].update({
|
||||
"edit_url": reverse('Settings:_project_state_change', args=(self.instance.pk,))
|
||||
})
|
||||
self.url_index_view = reverse('Settings:_project_states')
|
@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.0.8 on 2024-09-17 05:43
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project_management', '0007_project_priority'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='project',
|
||||
name='project_type',
|
||||
field=models.ForeignKey(blank=True, help_text='Type of project', null=True, on_delete=django.db.models.deletion.SET_NULL, to='project_management.projecttype', verbose_name='Project Type'),
|
||||
),
|
||||
]
|
@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.0.8 on 2024-09-17 05:45
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project_management', '0008_alter_project_project_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='project',
|
||||
name='state',
|
||||
field=models.ForeignKey(blank=True, help_text='State of the project', null=True, on_delete=django.db.models.deletion.SET_NULL, to='project_management.projectstate', verbose_name='Project State'),
|
||||
),
|
||||
]
|
@ -91,7 +91,7 @@ class Project(ProjectCommonFieldsName):
|
||||
|
||||
state = models.ForeignKey(
|
||||
ProjectState,
|
||||
blank= False,
|
||||
blank= True,
|
||||
help_text = 'State of the project',
|
||||
on_delete=models.SET_NULL,
|
||||
null = True,
|
||||
@ -101,7 +101,7 @@ class Project(ProjectCommonFieldsName):
|
||||
|
||||
project_type = models.ForeignKey(
|
||||
ProjectType,
|
||||
blank= False,
|
||||
blank= True,
|
||||
help_text = 'Type of project',
|
||||
on_delete=models.SET_NULL,
|
||||
null = True,
|
||||
|
@ -0,0 +1,42 @@
|
||||
{% extends 'detail.html.j2' %}
|
||||
|
||||
{% block additional-stylesheet %}
|
||||
{% load static %}
|
||||
<link rel="stylesheet" href="{% static 'ticketing.css' %}">
|
||||
{% endblock additional-stylesheet %}
|
||||
|
||||
{% load json %}
|
||||
{% load markdown %}
|
||||
|
||||
|
||||
{% block tabs %}
|
||||
|
||||
<div id="details" class="content-tab">
|
||||
|
||||
{% include 'content/section.html.j2' with tab=form.tabs.details %}
|
||||
|
||||
</div>
|
||||
|
||||
<div id="tasks" class="content-tab">
|
||||
|
||||
{% include 'content/section.html.j2' with tab=form.tabs.tasks %}
|
||||
|
||||
</div>
|
||||
|
||||
<div id="notes" class="content-tab">
|
||||
|
||||
{% include 'content/section.html.j2' with tab=form.tabs.notes %}
|
||||
|
||||
{{ notes_form }}
|
||||
<input type="submit" name="{{notes_form.prefix}}" value="Submit" />
|
||||
<div class="comments">
|
||||
{% if notes %}
|
||||
{% for note in notes %}
|
||||
{% include 'note.html.j2' %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -0,0 +1,48 @@
|
||||
{% extends 'base.html.j2' %}
|
||||
|
||||
{% block content_header_icon %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<input type="button" value="New Project State" onclick="window.location='{% url 'Settings:_project_state_add' %}';">
|
||||
|
||||
<table class="data">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Organization</th>
|
||||
<th>Created</th>
|
||||
<th>Modified</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
{% for project_state in project_states %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'Settings:_project_state_view' pk=project_state.id %}">{{ project_state.name }}</a>
|
||||
</td>
|
||||
<td>{% if project_state.is_global %}Global{% else %}{{ project_state.organization }}{% endif %}</td>
|
||||
<td>{{ project_state.created }}</td>
|
||||
<td>{{ project_state.modified }}</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
|
||||
<div class="pagination">
|
||||
<span class="step-links">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?page=1">« first</a>
|
||||
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
|
||||
{% endif %}
|
||||
|
||||
<span class="current">
|
||||
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
|
||||
</span>
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?page={{ page_obj.next_page_number }}">next</a>
|
||||
<a href="?page={{ page_obj.paginator.num_pages }}">last »</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
{% endblock %}
|
192
app/project_management/views/project_states.py
Normal file
192
app/project_management/views/project_states.py
Normal file
@ -0,0 +1,192 @@
|
||||
import json
|
||||
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Q
|
||||
from django.urls import reverse
|
||||
from django.views import generic
|
||||
|
||||
from access.mixin import OrganizationPermission
|
||||
|
||||
from core.forms.comment import AddNoteForm
|
||||
from core.models.notes import Notes
|
||||
from core.models.ticket.ticket import Ticket
|
||||
from core.views.common import AddView, ChangeView, DeleteView, DisplayView, IndexView
|
||||
|
||||
from project_management.forms.project_state import DetailForm, ProjectState, ProjectStateForm
|
||||
|
||||
from settings.models.user_settings import UserSettings
|
||||
|
||||
|
||||
|
||||
class Add(AddView):
|
||||
|
||||
form_class = ProjectStateForm
|
||||
|
||||
model = ProjectState
|
||||
|
||||
permission_required = [
|
||||
'project_management.add_projectstate',
|
||||
]
|
||||
|
||||
|
||||
def get_initial(self):
|
||||
|
||||
initial: dict = {
|
||||
'organization': UserSettings.objects.get(user = self.request.user).default_organization
|
||||
}
|
||||
|
||||
return initial
|
||||
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return reverse('Settings:_project_states')
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = 'New Group'
|
||||
|
||||
return context
|
||||
|
||||
|
||||
|
||||
class Change(ChangeView):
|
||||
|
||||
context_object_name = "project_task"
|
||||
|
||||
form_class = ProjectStateForm
|
||||
|
||||
model = ProjectState
|
||||
|
||||
permission_required = [
|
||||
'project_management.change_projectstate',
|
||||
]
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = self.object.title
|
||||
|
||||
return context
|
||||
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return reverse('Settings:_project_state_view', args=(self.kwargs['pk'],))
|
||||
|
||||
|
||||
|
||||
class Delete(DeleteView):
|
||||
|
||||
model = ProjectState
|
||||
|
||||
permission_required = [
|
||||
'project_management.delete_projectstate',
|
||||
]
|
||||
|
||||
template_name = 'form.html.j2'
|
||||
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
return reverse('Settings:_project_states')
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = 'Delete ' + self.object.name
|
||||
|
||||
return context
|
||||
|
||||
|
||||
|
||||
class Index(IndexView):
|
||||
|
||||
model = ProjectState
|
||||
|
||||
permission_required = [
|
||||
'project_management.view_projectstate',
|
||||
]
|
||||
|
||||
template_name = 'project_management/project_state_index.html.j2'
|
||||
|
||||
context_object_name = "project_states"
|
||||
|
||||
paginate_by = 10
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = 'Project States'
|
||||
|
||||
return context
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
|
||||
if self.request.user.is_superuser:
|
||||
|
||||
return self.model.objects.filter().order_by('name')
|
||||
|
||||
else:
|
||||
|
||||
return self.model.objects.filter(Q(organization__in=self.user_organizations()) | Q(is_global = True)).order_by('name')
|
||||
|
||||
|
||||
|
||||
class View(ChangeView):
|
||||
|
||||
model = ProjectState
|
||||
|
||||
permission_required = [
|
||||
'project_management.view_projectstate'
|
||||
]
|
||||
|
||||
template_name = 'project_management/project_state.html.j2'
|
||||
|
||||
form_class = DetailForm
|
||||
|
||||
context_object_name = "project"
|
||||
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['model_docs_path'] = self.model._meta.app_label + '/' + self.model._meta.model_name + '/'
|
||||
|
||||
context['model_pk'] = self.kwargs['pk']
|
||||
context['model_name'] = self.model._meta.verbose_name.replace(' ', '')
|
||||
|
||||
context['model_delete_url'] = reverse('Settings:_project_state_delete', kwargs={'pk': self.kwargs['pk']})
|
||||
|
||||
context['content_title'] = context['project'].name
|
||||
|
||||
return context
|
||||
|
||||
|
||||
# def post(self, request, *args, **kwargs):
|
||||
|
||||
# project = self.model.objects.get(pk=self.kwargs['pk'])
|
||||
|
||||
# notes = AddNoteForm(request.POST, prefix='note')
|
||||
|
||||
# if notes.is_bound and notes.is_valid() and notes.instance.note != '':
|
||||
|
||||
# if request.user.has_perm('core.add_notes'):
|
||||
|
||||
# notes.instance.organization = device.organization
|
||||
# notes.instance.project = project
|
||||
# notes.instance.usercreated = request.user
|
||||
|
||||
# notes.save()
|
||||
|
||||
# return super().post(request, *args, **kwargs)
|
@ -75,6 +75,13 @@ div#content article h3 {
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article style="">
|
||||
<h3>Project Management</h3>
|
||||
<ul>
|
||||
<li><a href="{% url 'Settings:_project_states' %}">Project States</a></li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -10,6 +10,8 @@ from itam.views import device_type, device_model, software_category
|
||||
|
||||
from itim.views import cluster_types, ports
|
||||
|
||||
from project_management.views import project_states
|
||||
|
||||
app_name = "Settings"
|
||||
urlpatterns = [
|
||||
|
||||
@ -59,6 +61,12 @@ urlpatterns = [
|
||||
path("port/<int:pk>/delete", ports.Delete.as_view(), name="_port_delete"),
|
||||
path("port/<int:pk>", ports.View.as_view(), name="_port_view"),
|
||||
|
||||
path("project_states", project_states.Index.as_view(), name="_project_states"),
|
||||
path("project_state/<int:pk>", project_states.View.as_view(), name="_project_state_view"),
|
||||
path("project_state/add", project_states.Add.as_view(), name="_project_state_add"),
|
||||
path("project_state/<int:pk>/edit", project_states.Change.as_view(), name="_project_state_change"),
|
||||
path("project_state/<int:pk>/delete", project_states.Delete.as_view(), name="_project_state_delete"),
|
||||
|
||||
path("software_category", software_categories.Index.as_view(), name="_software_categories"),
|
||||
path("software_category/<int:pk>", software_category.View.as_view(), name="_software_category_view"),
|
||||
path("software_category/add/", software_category.Add.as_view(), name="_software_category_add"),
|
||||
|
Reference in New Issue
Block a user