diff --git a/scipost_django/production/forms.py b/scipost_django/production/forms.py index e3db99be6bda6dcc3ff1fe1902b3dd6cc33d3275..d454ff12796dd6683c777a0af61295e0735fb693 100644 --- a/scipost_django/production/forms.py +++ b/scipost_django/production/forms.py @@ -8,10 +8,11 @@ from django import forms from django.contrib.auth import get_user_model from crispy_forms.helper import FormHelper -from crispy_forms.layout import Layout, Div, Field +from crispy_forms.layout import Layout, Div, Field, Submit from crispy_bootstrap5.bootstrap5 import FloatingField from journals.models import Journal +from markup.widgets import TextareaWithPreview from proceedings.models import Proceedings from scipost.fields import UserModelChoiceField @@ -29,6 +30,23 @@ today = datetime.datetime.today() class ProductionEventForm(forms.ModelForm): + class Meta: + model = ProductionEvent + fields = ("comments",) + widgets = { + "comments": TextareaWithPreview(attrs={"rows": 4}), + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.layout = Layout( + Field("comments"), + Submit("submit", "Submit"), + ) + + +class ProductionEventForm_deprec(forms.ModelForm): class Meta: model = ProductionEvent fields = ("comments",) diff --git a/scipost_django/production/templates/production/_hx_productionstream_details.html b/scipost_django/production/templates/production/_hx_productionstream_details.html index 3c7e4e60b61754abb7a74f0086e8235234efa816..a78ef59ea7d6c7cac8fd8272ecb3c29a5b285600 100644 --- a/scipost_django/production/templates/production/_hx_productionstream_details.html +++ b/scipost_django/production/templates/production/_hx_productionstream_details.html @@ -6,5 +6,21 @@ > {% include "production/_productionstream_details_summary_contents.html" with productionstream=productionstream %} </summary> - Stream details + + <button id="indicator-productionstream-{{ productionstream.id }}-details-contents" + class="htmx-indicator btn btn-sm btn-warning p-2" + type="button" + > + <small><strong>Loading...</strong></small> + <div class="spinner-grow spinner-grow-sm ms-2" role="status" aria-hidden="true"></div> + </button> + + <div id="productionstream-{{ productionstream.id }}-details-contents" + class="m-2 p-2 bg-white" + hx-get="{% url 'production:_hx_productionstream_details_contents' productionstream_id=productionstream.id %}" + hx-trigger="toggle once from:#productionstream-{{ productionstream.id }}-details" + hx-indicator="#indicator-productionstream-{{ productionstream.id }}-details-contents" + > + </div> + </details> diff --git a/scipost_django/production/templates/production/_hx_productionstream_details_contents.html b/scipost_django/production/templates/production/_hx_productionstream_details_contents.html new file mode 100644 index 0000000000000000000000000000000000000000..5e1506dc2b54867862199b93144ac8a79d8305d3 --- /dev/null +++ b/scipost_django/production/templates/production/_hx_productionstream_details_contents.html @@ -0,0 +1,18 @@ +{% load crispy_forms_tags %} + +<h3>Events</h3> +{% include 'production/_productionstream_events.html' with events=productionstream.events.all_without_duration %} + +<details class="border border-2 mt-4"> + <summary class="bg-light p-2">Add a comment to this stream</summary> + <div class="p-2"> + <form + hx-post="{% url 'production:_hx_events_add' productionstream_id=productionstream.id %}" + hx-target="#productionstream-{{ productionstream.id }}-details-contents" + > + <div id="productionevent-form"> + {% crispy productionevent_form %} + </div> + </form> + </div> +</details> diff --git a/scipost_django/production/templates/production/_production_events.html b/scipost_django/production/templates/production/_production_events.html index 2c7a41ddd8a385b1ceae7ccd29595d5c86811c8c..34b081ce8e503544e4ebec2df56a7b944def49a1 100644 --- a/scipost_django/production/templates/production/_production_events.html +++ b/scipost_django/production/templates/production/_production_events.html @@ -7,7 +7,7 @@ <div> <strong>{{ event.noted_by.user.first_name }} {{ event.noted_by.user.last_name }}</strong> <br> - {{ event.get_event_displa|linebreaksbr }} + {{ event.get_event_display|linebreaksbr }} </div> <div class="text-muted text-end d-flex justify-content-end"> <div> diff --git a/scipost_django/production/templates/production/_productionstream_events.html b/scipost_django/production/templates/production/_productionstream_events.html new file mode 100644 index 0000000000000000000000000000000000000000..06ac2746c111987e04410d9cbcd988355bf2ebba --- /dev/null +++ b/scipost_django/production/templates/production/_productionstream_events.html @@ -0,0 +1,67 @@ +{% load scipost_extras %} +{% load automarkup %} + +<table class="table table-bordered table-striped"> + <thead> + <tr> + <th>Date</th> + <th>Event</th> + <th>Noted by</th> + <th>Actions</th> + </tr> + </thead> + <tbody> + {% for event in events %} + <tr> + <td> + {{ event.noted_on }} + {% if event.duration %} + <br> + <strong>Duration: {{ event.duration|duration }}</strong> + {% endif %} + </td> + <td> + {{ event.get_event_display|linebreaksbr }} + </td> + <td> + <strong>{{ event.noted_by.user.first_name }} {{ event.noted_by.user.last_name }}</strong> + </td> + <td> + {% if not non_editable %} + {% if event.noted_by == request.user.production_user and event.editable %} + <div class="ps-2"> + <a href="{% url 'production:update_event' event.id %}"><span aria-hidden="true">{% include 'bi/pencil-square.html' %}</span></a> + <a class="text-danger" href="{% url 'production:delete_event' event.id %}"><span aria-hidden="true">{% include 'bi/trash-fill.html' %}</span></a> + </div> + {% endif %} + {% endif %} + </td> + </tr> + <tr> + <td colspan="4" class="ps-4 pb-4"> + {% if event.comments %} + <p class="mt-2 mb-0"> + {% if event.noted_to %} + <strong>To: {{ event.noted_to.user.first_name }} {{ event.noted_to.user.last_name }}</strong> + <br> + {% endif %} + {% automarkup event.comments %} + </p> + {% endif %} + + {% if event.attachments.exists %} + <ul> + {% for attachment in event.attachments.all %} + <li><a href="{{ attachment.get_absolute_url }}" target="_blank">Download Attachment {{ forloop.counter }}</a></li> + {% endfor %} + </ul> + {% endif %} + </td> + </tr> + {% empty %} + <tr> + <td>No events found</td> + </tr> + {% endfor %} + </tbody> +</table> diff --git a/scipost_django/production/urls.py b/scipost_django/production/urls.py index 5950e77ec2824b98d00cdb3fd81c8155c0deebe3..1f3b1302e828101b30ef9fab9a8009584f449f2b 100644 --- a/scipost_django/production/urls.py +++ b/scipost_django/production/urls.py @@ -20,6 +20,23 @@ urlpatterns = [ production_views._hx_productionstream_list, name="_hx_productionstream_list", ), + path( + "productionstreams/<int:productionstream_id>/", + include( + [ + path( + "details_contents", + production_views._hx_productionstream_details_contents, + name="_hx_productionstream_details_contents", + ), + path( + "events/add", + production_views._hx_events_add, + name="_hx_events_add", + ), + ] + ), + ), # end refactoring 2023-05 path("", production_views.production, name="production"), path("<int:stream_id>", production_views.production, name="production"), diff --git a/scipost_django/production/views.py b/scipost_django/production/views.py index 8cb9deb9e1cf485d1557bd44db0143819fcdbe42..a2d4adedea3c1c1fba93de23e26eebe9cf833ad0 100644 --- a/scipost_django/production/views.py +++ b/scipost_django/production/views.py @@ -33,6 +33,7 @@ from .models import ( from .forms import ( ProductionStreamSearchForm, ProductionEventForm, + ProductionEventForm_deprec, AssignOfficerForm, UserToOfficerForm, AssignSupervisorForm, @@ -75,6 +76,45 @@ def _hx_productionstream_list(request): return render(request, "production/_hx_productionstream_list.html", context) +@is_production_user() +@permission_required("scipost.can_view_production", raise_exception=True) +def _hx_productionstream_details_contents(request, productionstream_id): + stream = get_object_or_404(ProductionStream, pk=productionstream_id) + productionevent_form = ProductionEventForm() + context = { + "productionstream": stream, + "productionevent_form": productionevent_form, + } + return render( + request, + "production/_hx_productionstream_details_contents.html", + context, + ) + + +@is_production_user() +@permission_required("scipost.can_view_production", raise_exception=True) +def _hx_events_add(request, productionstream_id): + qs = ProductionStream.objects.ongoing() + if not request.user.has_perm("scipost.can_assign_production_officer"): + qs = qs.filter_for_user(request.user.production_user) + + productionstream = get_object_or_404(qs, pk=productionstream_id) + prodevent_form = ProductionEventForm(request.POST or None) + if prodevent_form.is_valid(): + prodevent = prodevent_form.save(commit=False) + prodevent.stream = productionstream + prodevent.noted_by = request.user.production_user + prodevent.save() + messages.success(request, "Comment added to Stream.") + else: + messages.warning(request, "The form was invalidly filled.") + return redirect(reverse( + "production:_hx_productionstream_details_contents", + kwargs={"productionstream_id": productionstream.id,}, + )) + + ################################ # 2023-05 HTMX refactoring end ################################ @@ -109,7 +149,7 @@ def production(request, stream_id=None): context["assign_officer_form"] = AssignOfficerForm() context["assign_invitiations_officer_form"] = AssignInvitationsOfficerForm() context["assign_supervisor_form"] = AssignSupervisorForm() - context["prodevent_form"] = ProductionEventForm() + context["prodevent_form"] = ProductionEventForm_deprec() if request.user.has_perm("scipost.can_view_all_production_streams"): types = constants.PRODUCTION_ALL_WORK_LOG_TYPES @@ -155,7 +195,7 @@ def stream(request, stream_id): # Restrict stream queryset if user is not supervisor streams = streams.filter_for_user(request.user.production_user) stream = get_object_or_404(streams, id=stream_id) - prodevent_form = ProductionEventForm() + prodevent_form = ProductionEventForm_deprec() assign_officer_form = AssignOfficerForm() assign_invitiations_officer_form = AssignInvitationsOfficerForm() assign_supervisor_form = AssignSupervisorForm() @@ -249,7 +289,7 @@ def add_event(request, stream_id): qs = qs.filter_for_user(request.user.production_user) stream = get_object_or_404(qs, pk=stream_id) - prodevent_form = ProductionEventForm(request.POST or None) + prodevent_form = ProductionEventForm_deprec(request.POST or None) if prodevent_form.is_valid(): prodevent = prodevent_form.save(commit=False) prodevent.stream = stream @@ -469,7 +509,7 @@ def remove_supervisor(request, stream_id, officer_id): ) class UpdateEventView(UpdateView): model = ProductionEvent - form_class = ProductionEventForm + form_class = ProductionEventForm_deprec slug_field = "id" slug_url_kwarg = "event_id"