mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +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 */ |     pointer-events: none; /* Prevent this div from blocking links underneath */ | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .notes { | ||||||
|  |     border-radius: 5px; | ||||||
|  |     background-color: #fafafa; | ||||||
|  |     padding: 5px; | ||||||
|  | } | ||||||
|  |  | ||||||
| .alert { | .alert { | ||||||
|     display: none; |     display: none; | ||||||
|     border-radius: 5px; |     border-radius: 5px; | ||||||
| @@ -853,6 +859,11 @@ input[type="submit"] { | |||||||
|     margin-right: 2px; |     margin-right: 2px; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .btn-small { | ||||||
|  |     padding: 3px; | ||||||
|  |     padding-left: 5px; | ||||||
|  | } | ||||||
|  |  | ||||||
| .btn-remove { | .btn-remove { | ||||||
|     padding: 3px; |     padding: 3px; | ||||||
|     padding-left: 5px; |     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" %} | {% extends "part/part_base.html" %} | ||||||
| {% load static %} | {% load static %} | ||||||
| {% load i18n %} | {% load i18n %} | ||||||
|  | {% load markdownify %} | ||||||
|  |  | ||||||
|  |  | ||||||
| {% block menubar %} | {% block menubar %} | ||||||
| @@ -135,11 +136,38 @@ | |||||||
|         {% endif %} |         {% endif %} | ||||||
|         {% if part.responsible %} |         {% if part.responsible %} | ||||||
|         <tr> |         <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><strong>{% trans "Responsible User" %}</strong></td> | ||||||
|             <td>{{ part.responsible }}</td>  |             <td>{{ part.responsible }}</td>  | ||||||
|         </tr> |         </tr> | ||||||
|         {% endif %} |         {% 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> |     </table> | ||||||
|     </div> |     </div> | ||||||
|     <div class='col-sm-6'> |     <div class='col-sm-6'> | ||||||
| @@ -240,11 +268,11 @@ | |||||||
|  |  | ||||||
| {% block post_content_panel %} | {% block post_content_panel %} | ||||||
|  |  | ||||||
| <div class='panel panel-default panel-inventree'> | <div class='row'> | ||||||
|  |     <div class='col-sm-6'> | ||||||
|  |         <div class='panel panel-default panel-inventree'> | ||||||
|             <div class='panel-heading'> |             <div class='panel-heading'> | ||||||
|         <h4> |                 <h4>{% trans "Parameters" %}</h4> | ||||||
|             {% trans "Part Parameters" %} |  | ||||||
|         </h4> |  | ||||||
|             </div> |             </div> | ||||||
|             <div class='panel-content'> |             <div class='panel-content'> | ||||||
|                 <div id='param-button-toolbar'> |                 <div id='param-button-toolbar'> | ||||||
| @@ -258,6 +286,18 @@ | |||||||
|                 </div> |                 </div> | ||||||
|                 <table id='parameter-table' class='table table-condensed table-striped' data-toolbar="#param-button-toolbar"></table> |                 <table id='parameter-table' class='table table-condensed table-striped' data-toolbar="#param-button-toolbar"></table> | ||||||
|             </div> |             </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> | ||||||
|  |     </div> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
| {% endblock %} | {% endblock %} | ||||||
| @@ -269,6 +309,18 @@ | |||||||
| {% block js_ready %} | {% block js_ready %} | ||||||
|     {{ block.super }} |     {{ 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() { |     $(".slidey").change(function() { | ||||||
|         var field = $(this).attr('fieldname'); |         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 %} | {% endblock %} | ||||||
|   | |||||||
| @@ -109,16 +109,4 @@ | |||||||
|         </a> |         </a> | ||||||
|     </li> |     </li> | ||||||
|     {% endif %} |     {% 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> | </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'^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'^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'^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'), |     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.shortcuts import HttpResponseRedirect | ||||||
| from django.utils.translation import gettext_lazy as _ | from django.utils.translation import gettext_lazy as _ | ||||||
| from django.urls import reverse, reverse_lazy | 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.models import model_to_dict | ||||||
| from django.forms import HiddenInput, CheckboxInput | from django.forms import HiddenInput, CheckboxInput | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| @@ -747,40 +747,6 @@ class PartImportAjax(FileManagementAjaxView, PartImport): | |||||||
|         return PartImport.validate(self, self.steps.current, form, **kwargs) |         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): | class PartDetail(InvenTreeRoleMixin, DetailView): | ||||||
|     """ Detail view for Part object |     """ Detail view for Part object | ||||||
|     """ |     """ | ||||||
|   | |||||||
| @@ -353,12 +353,16 @@ function constructFormBody(fields, options) { | |||||||
|             // Override existing query filters (if provided!) |             // Override existing query filters (if provided!) | ||||||
|             fields[field].filters = Object.assign(fields[field].filters || {}, field_options.filters); |             fields[field].filters = Object.assign(fields[field].filters || {}, field_options.filters); | ||||||
|  |  | ||||||
|  |             // TODO: Refactor the following code with Object.assign (see above) | ||||||
|  |  | ||||||
|             // Secondary modal options |             // Secondary modal options | ||||||
|             fields[field].secondary = field_options.secondary; |             fields[field].secondary = field_options.secondary; | ||||||
|  |  | ||||||
|             // Edit callback |             // Edit callback | ||||||
|             fields[field].onEdit = field_options.onEdit; |             fields[field].onEdit = field_options.onEdit; | ||||||
|  |  | ||||||
|  |             fields[field].multiline = field_options.multiline; | ||||||
|  |  | ||||||
|             // Custom help_text |             // Custom help_text | ||||||
|             if (field_options.help_text) { |             if (field_options.help_text) { | ||||||
|                 fields[field].help_text = 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}'`); |         opts.push(`placeholder='${parameters.placeholder}'`); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (parameters.multiline) { | ||||||
|  |         return `<textarea ${opts.join(' ')}></textarea>`; | ||||||
|  |     } else { | ||||||
|         return `<input ${opts.join(' ')}>`; |         return `<input ${opts.join(' ')}>`; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user