mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 05:05:42 +00:00 
			
		
		
		
	Merge pull request #1808 from SchrodingersGat/part-page-refactor
Move "attachments" and "notes" to "Part Detail" page
This commit is contained in:
		| @@ -837,6 +837,12 @@ input[type="submit"] { | ||||
|     pointer-events: none; /* Prevent this div from blocking links underneath */ | ||||
| } | ||||
|  | ||||
| .notes { | ||||
|     border-radius: 5px; | ||||
|     background-color: #fafafa; | ||||
|     padding: 5px; | ||||
| } | ||||
|  | ||||
| .alert { | ||||
|     display: none; | ||||
|     border-radius: 5px; | ||||
| @@ -853,6 +859,11 @@ input[type="submit"] { | ||||
|     margin-right: 2px; | ||||
| } | ||||
|  | ||||
| .btn-small { | ||||
|     padding: 3px; | ||||
|     padding-left: 5px; | ||||
| } | ||||
|  | ||||
| .btn-remove { | ||||
|     padding: 3px; | ||||
|     padding-left: 5px; | ||||
|   | ||||
| @@ -1,86 +0,0 @@ | ||||
| {% extends "part/part_base.html" %} | ||||
| {% load static %} | ||||
| {% load i18n %} | ||||
|  | ||||
| {% block menubar %} | ||||
| {% include 'part/navbar.html' with tab='attachments' %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block heading %} | ||||
| {% trans "Part Attachments" %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block details %} | ||||
|  | ||||
| {% include "attachment_table.html" with attachments=part.part_attachments %} | ||||
|  | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js_ready %} | ||||
| {{ block.super }} | ||||
|  | ||||
|     loadAttachmentTable( | ||||
|         '{% url "api-part-attachment-list" %}', | ||||
|         { | ||||
|             filters: { | ||||
|                 part: {{ part.pk }}, | ||||
|             }, | ||||
|             onEdit: function(pk) { | ||||
|                 var url = `/api/part/attachment/${pk}/`; | ||||
|  | ||||
|                 constructForm(url, { | ||||
|                     fields: { | ||||
|                         comment: {}, | ||||
|                     }, | ||||
|                     title: '{% trans "Edit Attachment" %}', | ||||
|                     onSuccess: reloadAttachmentTable, | ||||
|                 }); | ||||
|             }, | ||||
|             onDelete: function(pk) { | ||||
|                 var url = `/api/part/attachment/${pk}/`; | ||||
|  | ||||
|                 constructForm(url, { | ||||
|                     method: 'DELETE', | ||||
|                     confirmMessage: '{% trans "Confirm Delete Operation" %}', | ||||
|                     title: '{% trans "Delete Attachment" %}', | ||||
|                     onSuccess: reloadAttachmentTable, | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     ); | ||||
|  | ||||
|     enableDragAndDrop( | ||||
|         '#attachment-dropzone', | ||||
|         '{% url "api-part-attachment-list" %}', | ||||
|         { | ||||
|             data: { | ||||
|                 part: {{ part.id }}, | ||||
|             }, | ||||
|             label: 'attachment', | ||||
|             success: function(data, status, xhr) { | ||||
|                 reloadAttachmentTable(); | ||||
|             } | ||||
|         } | ||||
|     ); | ||||
|  | ||||
|     $("#new-attachment").click(function() { | ||||
|  | ||||
|         constructForm( | ||||
|             '{% url "api-part-attachment-list" %}', | ||||
|             { | ||||
|                 method: 'POST', | ||||
|                 fields: { | ||||
|                     attachment: {}, | ||||
|                     comment: {}, | ||||
|                     part: { | ||||
|                         value: {{ part.pk }}, | ||||
|                         hidden: true, | ||||
|                     } | ||||
|                 }, | ||||
|                 onSuccess: reloadAttachmentTable, | ||||
|                 title: '{% trans "Add Attachment" %}', | ||||
|             } | ||||
|         ) | ||||
|     }); | ||||
|  | ||||
| {% endblock %} | ||||
| @@ -1,6 +1,7 @@ | ||||
| {% extends "part/part_base.html" %} | ||||
| {% load static %} | ||||
| {% load i18n %} | ||||
| {% load markdownify %} | ||||
|  | ||||
|  | ||||
| {% block menubar %} | ||||
| @@ -135,11 +136,38 @@ | ||||
|         {% endif %} | ||||
|         {% if part.responsible %} | ||||
|         <tr> | ||||
|             <td><span class='fas fa-user'>d</span></td> | ||||
|             <td><span class='fas fa-user'></span></td> | ||||
|             <td><strong>{% trans "Responsible User" %}</strong></td> | ||||
|             <td>{{ part.responsible }}</td>  | ||||
|         </tr> | ||||
|         {% endif %} | ||||
|  | ||||
|         <tr><td colspan="3"></td></tr> | ||||
|  | ||||
|         <tr> | ||||
|             <td><span class='fas fa-sticky-note'></span></td> | ||||
|             <td> | ||||
|                 <strong>{% trans "Notes" %}</strong> | ||||
|             </td> | ||||
|             <td> | ||||
|                 <div class='btn-group float-right'> | ||||
|                     <button type='button' id='edit-notes' title='{% trans "Edit Notes" %}' class='btn btn-small btn-default'> | ||||
|                         <span class='fas fa-edit'>       | ||||
|                         </span> | ||||
|                     </button> | ||||
|                 </div> | ||||
|             </td> | ||||
|         </tr> | ||||
|         <tr> | ||||
|             <td colspan='3'> | ||||
|                 {% if part.notes %} | ||||
|                 <div class='notes'> | ||||
|                     {{ part.notes | markdownify }} | ||||
|                 </div> | ||||
|                 {% endif %} | ||||
|             </td> | ||||
|         </tr> | ||||
|  | ||||
|     </table> | ||||
|     </div> | ||||
|     <div class='col-sm-6'> | ||||
| @@ -240,23 +268,35 @@ | ||||
|  | ||||
| {% block post_content_panel %} | ||||
|  | ||||
| <div class='panel panel-default panel-inventree'> | ||||
|     <div class='panel-heading'> | ||||
|         <h4> | ||||
|             {% trans "Part Parameters" %} | ||||
|         </h4> | ||||
|     </div> | ||||
|     <div class='panel-content'> | ||||
|         <div id='param-button-toolbar'> | ||||
|             <div class='button-toolbar container-fluid' style='float: right;'> | ||||
|                 {% if roles.part.add %} | ||||
|                 <button title='{% trans "Add new parameter" %}' class='btn btn-success' id='param-create'> | ||||
|                     <span class='fas fa-plus-circle'></span> {% trans "New Parameter" %} | ||||
|                 </button> | ||||
|                 {% endif %} | ||||
| <div class='row'> | ||||
|     <div class='col-sm-6'> | ||||
|         <div class='panel panel-default panel-inventree'> | ||||
|             <div class='panel-heading'> | ||||
|                 <h4>{% trans "Parameters" %}</h4> | ||||
|             </div> | ||||
|             <div class='panel-content'> | ||||
|                 <div id='param-button-toolbar'> | ||||
|                     <div class='button-toolbar container-fluid' style='float: right;'> | ||||
|                         {% if roles.part.add %} | ||||
|                         <button title='{% trans "Add new parameter" %}' class='btn btn-success' id='param-create'> | ||||
|                             <span class='fas fa-plus-circle'></span> {% trans "New Parameter" %} | ||||
|                         </button> | ||||
|                         {% endif %} | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <table id='parameter-table' class='table table-condensed table-striped' data-toolbar="#param-button-toolbar"></table> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class='col-sm-6'> | ||||
|         <div class='panel panel-default panel-inventree'> | ||||
|             <div class='panel-heading'> | ||||
|                 <h4>{% trans "Attachments" %}</h4> | ||||
|             </div> | ||||
|             <div class='panel-content'> | ||||
|                 {% include "attachment_table.html" %} | ||||
|             </div> | ||||
|         </div> | ||||
|         <table id='parameter-table' class='table table-condensed table-striped' data-toolbar="#param-button-toolbar"></table> | ||||
|     </div> | ||||
| </div> | ||||
|  | ||||
| @@ -269,6 +309,18 @@ | ||||
| {% block js_ready %} | ||||
|     {{ block.super }} | ||||
|  | ||||
|     $('#edit-notes').click(function() { | ||||
|         constructForm('{% url "api-part-detail" part.pk %}', { | ||||
|             fields: { | ||||
|                 notes: { | ||||
|                     multiline: true, | ||||
|                 } | ||||
|             }, | ||||
|             title: '{% trans "Edit Part Notes" %}', | ||||
|             reload: true, | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     $(".slidey").change(function() { | ||||
|         var field = $(this).attr('fieldname'); | ||||
|  | ||||
| @@ -337,4 +389,68 @@ | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     loadAttachmentTable( | ||||
|         '{% url "api-part-attachment-list" %}', | ||||
|         { | ||||
|             filters: { | ||||
|                 part: {{ part.pk }}, | ||||
|             }, | ||||
|             onEdit: function(pk) { | ||||
|                 var url = `/api/part/attachment/${pk}/`; | ||||
|  | ||||
|                 constructForm(url, { | ||||
|                     fields: { | ||||
|                         comment: {}, | ||||
|                     }, | ||||
|                     title: '{% trans "Edit Attachment" %}', | ||||
|                     onSuccess: reloadAttachmentTable, | ||||
|                 }); | ||||
|             }, | ||||
|             onDelete: function(pk) { | ||||
|                 var url = `/api/part/attachment/${pk}/`; | ||||
|  | ||||
|                 constructForm(url, { | ||||
|                     method: 'DELETE', | ||||
|                     confirmMessage: '{% trans "Confirm Delete Operation" %}', | ||||
|                     title: '{% trans "Delete Attachment" %}', | ||||
|                     onSuccess: reloadAttachmentTable, | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     ); | ||||
|  | ||||
|     enableDragAndDrop( | ||||
|         '#attachment-dropzone', | ||||
|         '{% url "api-part-attachment-list" %}', | ||||
|         { | ||||
|             data: { | ||||
|                 part: {{ part.id }}, | ||||
|             }, | ||||
|             label: 'attachment', | ||||
|             success: function(data, status, xhr) { | ||||
|                 reloadAttachmentTable(); | ||||
|             } | ||||
|         } | ||||
|     ); | ||||
|  | ||||
|     $("#new-attachment").click(function() { | ||||
|  | ||||
|         constructForm( | ||||
|             '{% url "api-part-attachment-list" %}', | ||||
|             { | ||||
|                 method: 'POST', | ||||
|                 fields: { | ||||
|                     attachment: {}, | ||||
|                     comment: {}, | ||||
|                     part: { | ||||
|                         value: {{ part.pk }}, | ||||
|                         hidden: true, | ||||
|                     } | ||||
|                 }, | ||||
|                 onSuccess: reloadAttachmentTable, | ||||
|                 title: '{% trans "Add Attachment" %}', | ||||
|             } | ||||
|         ) | ||||
|     }); | ||||
|  | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -109,16 +109,4 @@ | ||||
|         </a> | ||||
|     </li> | ||||
|     {% endif %} | ||||
|     <li class='list-group-item {% if tab == "attachments" %}active{% endif %}' title='{% trans "Attachments" %}'> | ||||
|         <a href='{% url "part-attachments" part.id %}'> | ||||
|             <span class='menu-tab-icon fas fa-paperclip sidebar-icon'></span> | ||||
|             {% trans "Attachments" %} | ||||
|         </a> | ||||
|     </li> | ||||
|     <li class='list-group-item {% if tab == "notes" %}active{% endif %}' title='{% trans "Part Notes" %}'> | ||||
|         <a href='{% url "part-notes" part.id %}'> | ||||
|             <span class='menu-tab-icon fas fa-clipboard sidebar-icon'></span> | ||||
|             {% trans "Notes" %} | ||||
|         </a> | ||||
|     </li> | ||||
| </ul> | ||||
|   | ||||
| @@ -1,57 +0,0 @@ | ||||
| {% extends "part/part_base.html" %} | ||||
| {% load static %} | ||||
| {% load crispy_forms_tags %} | ||||
| {% load i18n %} | ||||
| {% load markdownify %} | ||||
|  | ||||
| {% block menubar %} | ||||
| {% include 'part/navbar.html' with tab='notes' %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block heading %} | ||||
| {% trans "Part Notes" %} | ||||
| {% if roles.part.change and not editing %} | ||||
| <button title='{% trans "Edit notes" %}' class='btn btn-default' id='edit-notes'><span class='fas fa-edit'></span></button> | ||||
| {% endif %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block details %} | ||||
|  | ||||
| {% if editing %} | ||||
| <form method='POST'> | ||||
|     {% csrf_token %} | ||||
|  | ||||
|     {{ form }} | ||||
|     <hr> | ||||
|  | ||||
|     <button type="submit" class='btn btn-default'>{% trans "Save" %}</button> | ||||
|  | ||||
| </form> | ||||
|  | ||||
| {{ form.media }} | ||||
|  | ||||
| {% else %} | ||||
|  | ||||
| <div class='panel panel-default'> | ||||
|     {% if part.notes %} | ||||
|     <div class='panel-content'> | ||||
|         {{ part.notes | markdownify }} | ||||
|     </div> | ||||
|     {% endif %} | ||||
| </div> | ||||
|  | ||||
| {% endif %} | ||||
|  | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js_ready %} | ||||
| {{ block.super }} | ||||
|  | ||||
| {% if editing %} | ||||
| {% else %} | ||||
| $("#edit-notes").click(function() { | ||||
|     location.href = "{% url 'part-notes' part.id %}?edit=1"; | ||||
| }); | ||||
| {% endif %} | ||||
|  | ||||
| {% endblock %} | ||||
| @@ -61,8 +61,6 @@ part_detail_urls = [ | ||||
|     url(r'^tests/', views.PartDetail.as_view(template_name='part/part_tests.html'), name='part-test-templates'), | ||||
|     url(r'^track/?', views.PartDetail.as_view(template_name='part/track.html'), name='part-track'), | ||||
|     url(r'^related-parts/?', views.PartDetail.as_view(template_name='part/related.html'), name='part-related'), | ||||
|     url(r'^attachments/?', views.PartDetail.as_view(template_name='part/attachments.html'), name='part-attachments'), | ||||
|     url(r'^notes/?', views.PartNotes.as_view(), name='part-notes'), | ||||
|  | ||||
|     url(r'^qr_code/?', views.PartQRCode.as_view(), name='part-qr'), | ||||
|  | ||||
|   | ||||
| @@ -13,7 +13,7 @@ from django.shortcuts import get_object_or_404 | ||||
| from django.shortcuts import HttpResponseRedirect | ||||
| from django.utils.translation import gettext_lazy as _ | ||||
| from django.urls import reverse, reverse_lazy | ||||
| from django.views.generic import DetailView, ListView, UpdateView | ||||
| from django.views.generic import DetailView, ListView | ||||
| from django.forms.models import model_to_dict | ||||
| from django.forms import HiddenInput, CheckboxInput | ||||
| from django.conf import settings | ||||
| @@ -747,40 +747,6 @@ class PartImportAjax(FileManagementAjaxView, PartImport): | ||||
|         return PartImport.validate(self, self.steps.current, form, **kwargs) | ||||
|  | ||||
|  | ||||
| class PartNotes(UpdateView): | ||||
|     """ View for editing the 'notes' field of a Part object. | ||||
|     Presents a live markdown editor. | ||||
|     """ | ||||
|  | ||||
|     context_object_name = 'part' | ||||
|     # form_class = part_forms.EditNotesForm | ||||
|     template_name = 'part/notes.html' | ||||
|     model = Part | ||||
|  | ||||
|     role_required = 'part.change' | ||||
|  | ||||
|     fields = ['notes'] | ||||
|  | ||||
|     def get_success_url(self): | ||||
|         """ Return the success URL for this form """ | ||||
|  | ||||
|         return reverse('part-notes', kwargs={'pk': self.get_object().id}) | ||||
|  | ||||
|     def get_context_data(self, **kwargs): | ||||
|  | ||||
|         part = self.get_object() | ||||
|  | ||||
|         context = super().get_context_data(**kwargs) | ||||
|  | ||||
|         context['editing'] = str2bool(self.request.GET.get('edit', '')) | ||||
|  | ||||
|         ctx = part.get_context_data(self.request) | ||||
|  | ||||
|         context.update(ctx) | ||||
|  | ||||
|         return context | ||||
|  | ||||
|  | ||||
| class PartDetail(InvenTreeRoleMixin, DetailView): | ||||
|     """ Detail view for Part object | ||||
|     """ | ||||
|   | ||||
| @@ -353,12 +353,16 @@ function constructFormBody(fields, options) { | ||||
|             // Override existing query filters (if provided!) | ||||
|             fields[field].filters = Object.assign(fields[field].filters || {}, field_options.filters); | ||||
|  | ||||
|             // TODO: Refactor the following code with Object.assign (see above) | ||||
|  | ||||
|             // Secondary modal options | ||||
|             fields[field].secondary = field_options.secondary; | ||||
|  | ||||
|             // Edit callback | ||||
|             fields[field].onEdit = field_options.onEdit; | ||||
|  | ||||
|             fields[field].multiline = field_options.multiline; | ||||
|  | ||||
|             // Custom help_text | ||||
|             if (field_options.help_text) { | ||||
|                 fields[field].help_text = field_options.help_text; | ||||
| @@ -1483,7 +1487,11 @@ function constructInputOptions(name, classes, type, parameters) { | ||||
|         opts.push(`placeholder='${parameters.placeholder}'`); | ||||
|     } | ||||
|  | ||||
|     return `<input ${opts.join(' ')}>`; | ||||
|     if (parameters.multiline) { | ||||
|         return `<textarea ${opts.join(' ')}></textarea>`; | ||||
|     } else { | ||||
|         return `<input ${opts.join(' ')}>`; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user