Skip to content

Commit

Permalink
Separate SOW & PAF form editing (#4261)
Browse files Browse the repository at this point in the history
Fixes #4204. Moves the SOW out into it's own form when editing. This
also removes the `user_has_updated_details` attribute in favor of using
a property for both the project form & SOW that checks the field_data on
the respective form. This allows for tracking of the project form & SOW
independently.

Also a few small aesthetic changes bundled in, like margin additions &
hiding of submission attachments sidebar when there's none.

---------

Co-authored-by: Fredrik Jonsson <[email protected]>
  • Loading branch information
wes-otf and frjo authored Dec 17, 2024
1 parent eb2116f commit 6ca172a
Show file tree
Hide file tree
Showing 21 changed files with 396 additions and 239 deletions.
11 changes: 9 additions & 2 deletions hypha/apply/funds/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
from hypha.apply.projects.forms import ProjectCreateForm
from hypha.apply.review.models import Review
from hypha.apply.stream_forms.blocks import GroupToggleBlock
from hypha.apply.todo.options import PROJECT_WAITING_PAF
from hypha.apply.todo.options import PROJECT_WAITING_PF, PROJECT_WAITING_SOW
from hypha.apply.todo.views import add_task_to_user
from hypha.apply.users.decorators import (
is_apply_staff,
Expand Down Expand Up @@ -286,10 +286,17 @@ def post(self, *args, **kwargs):
)
# add task for staff to add PAF to the project
add_task_to_user(
code=PROJECT_WAITING_PAF,
code=PROJECT_WAITING_PF,
user=project.lead,
related_obj=project,
)
if self.submission.page.specific.sow_forms.first():
# Add SOW task if one exists on the parent
add_task_to_user(
code=PROJECT_WAITING_SOW,
user=project.lead,
related_obj=project,
)
return HttpResponseClientRedirect(project.get_absolute_url())
return render(
self.request,
Expand Down
3 changes: 1 addition & 2 deletions hypha/apply/projects/forms/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,13 @@ def clean(self):
return cleaned_data

def save(self, *args, **kwargs):
self.instance.form_fields = kwargs.pop("paf_form_fields", {})
self.instance.form_fields = kwargs.pop("pf_form_fields", {})
self.instance.form_data = {
field: self.cleaned_data[field]
for field in self.instance.question_field_ids
if field in self.cleaned_data
}
self.instance.process_file_data(self.cleaned_data)
self.instance.user_has_updated_details = True
return super().save(*args, **kwargs)


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Generated by Django 4.2.16 on 2024-12-06 16:39

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("application_projects", "0093_remove_reportversion_form_fields"),
]

operations = [
migrations.RemoveField(
model_name="project",
name="user_has_updated_details",
),
]
19 changes: 16 additions & 3 deletions hypha/apply/projects/models/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,6 @@ class Project(BaseStreamForm, AccessFormData, models.Model):
# tracks read/write state of the Project
is_locked = models.BooleanField(default=False)

# tracks updates to the Projects fields via the Project Application Form.
user_has_updated_details = models.BooleanField(default=False)
submitted_contract_documents = models.BooleanField(
"Submit Contracting Documents", default=False
)
Expand Down Expand Up @@ -412,6 +410,21 @@ def editable_by(self, user):
return True
return False

@property
def user_has_updated_pf_details(self) -> bool:
"""Determines if the user has updated the Project Form"""
return bool(self.form_fields)

@property
def user_has_updated_sow_details(self) -> bool | None:
"""Determines if the user has updated the SOW form
If there is no configured SOW, None will be returned
"""
if self.submission.page.specific.sow_forms.first() and hasattr(self, "sow"):
return bool(self.sow.form_data)
return None

@property
def editable(self):
if self.is_locked:
Expand Down Expand Up @@ -456,7 +469,7 @@ def can_send_for_approval(self):
being locked.
"""
correct_state = self.status == DRAFT and not self.is_locked
return correct_state and self.user_has_updated_details
return correct_state and self.user_has_updated_pf_details

@property
def is_in_progress(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,44 +77,61 @@ <h2 class="text-lg font-semibold m-0">

<li class="docs-block__row">
<div class="docs-block__row-inner">
{% if object.user_has_updated_details %}
{% if object.user_has_updated_pf_details %}
{% heroicon_outline "check-circle" class="stroke-light-blue me-1" aria_hidden=true %}
{% else %}
{% heroicon_outline "check-circle" class="stroke-gray-400 me-1" aria_hidden=true %}
{% endif %}
<p class="docs-block__title">{% trans "Project Form" %}</p>
<p class="docs-block__title">{% trans "Project form" %}</p>
</div>
<div class="docs-block__row-inner">
{% user_can_edit_paf object user as can_edit_paf %}
{% if can_edit_paf %}
<a class="{% if not object.user_has_updated_details %}button button--project-action{% else %}docs-block__icon-link{% endif %}" href="{% url 'apply:projects:edit' pk=object.pk %}">
{% if object.user_has_updated_details %}
{% user_can_edit_pfs object user as can_edit_pfs %}
{% if can_edit_pfs %}
<a class="{% if not object.user_has_updated_pf_details %}button button--project-action{% else %}docs-block__icon-link{% endif %}" href="{% url 'apply:projects:edit_pf' pk=object.pk %}">
{% if object.user_has_updated_pf_details %}
{% heroicon_micro "pencil-square" class="inline me-1 w-4 h-4" aria_hidden=true %}
{% trans "Edit" %}
{% else %}
{% trans "Fill in" %}
{% endif %}
</a>
{% endif %}
{% if object.user_has_updated_details and not user.is_applicant %}
{% if object.user_has_updated_pf_details and not user.is_applicant %}
<a class="docs-block__icon-link" href="{% url 'apply:projects:approval' pk=project.pk %}">
{% heroicon_micro "eye" class="inline me-1 w-4 h-4" aria_hidden=true %}
{% trans "View" %}
</a>
{% endif %}
</div>
{% has_project_sow_form object as project_sow %}
{% if project_sow and object.user_has_updated_details and not user.is_applicant %}
<ul class="mt-2 w-full ps-7">
{% if project_sow and not user.is_applicant %}
<ul class="mt-2 w-full">
<li class="docs-block__document">
{% if object.user_has_updated_sow_details %}
{% heroicon_outline "check-circle" class="stroke-light-blue me-1" aria_hidden=true %}
{% else %}
{% heroicon_outline "check-circle" class="stroke-gray-400 me-1" aria_hidden=true %}
{% endif %}
<div class="docs-block__document-inner">
<p class="docs-block__document-info">{% trans "Scope of work (SOW)" %}</p>
<p class="docs-block__document-info">{% trans "Scope of work" %}</p>
</div>
<div class="docs-block__document-inner__actions">
<a class="docs-block__icon-link" href="{% url 'apply:projects:sow' pk=project.pk %}">
{% heroicon_micro "eye" class="inline me-1 w-4 h-4" aria_hidden=true %}
{% trans "View" %}
</a>
{% if can_edit_pfs %}
<a class="{% if not object.user_has_updated_sow_details %}button button--project-action{% else %}docs-block__icon-link{% endif %}" href="{% url 'apply:projects:edit_sow' pk=object.pk %}">
{% if object.user_has_updated_sow_details %}
{% heroicon_micro "pencil-square" class="inline me-1 w-4 h-4" aria_hidden=true %}
{% trans "Edit" %}
{% else %}
{% trans "Fill in" %}
{% endif %}
</a>
{% endif %}
{% if object.user_has_updated_sow_details %}
<a class="docs-block__icon-link" href="{% url 'apply:projects:sow' pk=project.pk %}">
{% heroicon_micro "eye" class="inline me-1 w-4 h-4" aria_hidden=true %}
{% trans "View" %}
</a>
{% endif %}
</div>
</li>
</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ <h1>{{ org_name }} {% trans "Project Form" %}</h1>
<p>{{ field_value|safe|default:"-" }}</p>
{% endfor %}

<!-- SOW fields data in paragraph format-->
{% for field_name, field_value in sow_data.items %}
<p><b>{{ field_name }}</b></p>
<p>{{ field_value|safe|default:"-" }}</p>
{% endfor %}

<!-- Approvers data in list format -->
<h2>{% trans "Approvals" %}</h2>
{% if approvals %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ <h4 class="mb-2">{% trans "Supporting Documents" %}</h4>
<aside class="sidebar sidebar__project">
<div class="sidebar__inner sidebar__inner--light-blue sidebar__inner--actions" data-testid="sidebar-primary-actions">
<h5>{% trans "Actions to take" %}</h5>
{% user_can_edit_paf object user as can_edit_paf %}
{% if can_edit_paf %}
<a class="button button--bottom-space button--primary button--full-width {% if user_can_approve %} is-disabled {% endif %}" href="{% url 'apply:projects:edit' pk=object.pk %}">{% trans "Edit PAF" %}</a>
{% user_can_edit_pfs object user as can_edit_pfs %}
{% if can_edit_pfs %}
<a class="button button--bottom-space button--primary button--full-width {% if user_can_approve %} is-disabled {% endif %}" href="{% url 'apply:projects:edit_pf' pk=object.pk %}">{% trans "Edit" %}</a>
{% endif %}
<div x-data="{ show: false }" class="relative">
<button x-on:click="show = ! show" class="button button--bottom-space button--primary button--full-width" type="button">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,14 @@
{% endadminbar %}

{% if approval_form_exists %}

{% include "forms/includes/form_errors.html" with form=paf_form %}
{% if sow_form_exists %}
{% include "forms/includes/form_errors.html" with form=sow_form %}
{% endif %}
{% include "forms/includes/form_errors.html" with form=pf_form %}

<div class="pt-8 pb-8 mx-auto mt-4 mb-12 wrapper wrapper--default-bg wrapper--sidebar ps-20 ">
<div class="wrapper--sidebar--inner">
<form class="form application-form" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ paf_form.media }}
{% if sow_form_exists %}
{{ sow_form.media }}
{% endif %}

{% for field in paf_form %}
{{ pf_form.media }}
{% for field in pf_form %}
{% if field.field %}
{% if field.field.multi_input_field %}
{% include "forms/includes/multi_input_field.html" %}
Expand All @@ -41,43 +33,25 @@
{% endfor %}

{# Hidden fields needed e.g. for django-file-form. See `StreamBaseForm.hidden_fields` #}
{% for hidden_field in paf_form.hidden_fields %}
{% for hidden_field in pf_form.hidden_fields %}
{{ hidden_field }}
{% endfor %}

{% if sow_form_exists %}
<hr class="mt-6 border-2 border-dotted">
{% for field in sow_form %}
{% if field.field %}
{% if field.field.multi_input_field %}
{% include "forms/includes/multi_input_field.html" %}
{% else %}
{% include "forms/includes/field.html" %}
{% endif %}
{% else %}
{{ field.block }}
{% endif %}
{% endfor %}


{% for hidden_field in sow_form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
{% endif %}

{% trans "Save draft" as save_draft %}
{% for button_name, button_type, button_value in buttons %}
<button class="button button--submit button--top-space button--{{ button_type }}" type="submit" name="{{ button_name }}" {% if button_value == save_draft %}formnovalidate{% endif %}>{{ button_value }}</button>
{% endfor %}
</form>
</div>
<aside class="sidebar sidebar__project">
<div class="sidebar__inner sidebar__inner--actions">
<h5>{% trans "Proposal attachments" %}</h5>
{% for file in submissions_attachments %}
<p><b><a href="{{ file.url }}" target="_blank">{{ file.filename }}</a></b></p>
{% endfor %}
</div>
{% if submissions_attachments %}
<div class="sidebar__inner sidebar__inner--actions">
<h5>{% trans "Proposal attachments" %}</h5>
{% for file in submissions_attachments %}
<p><b><a href="{{ file.url }}" target="_blank">{{ file.filename }}</a></b></p>
{% endfor %}
</div>
{% endif %}
</aside>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<div class="wrapper wrapper--large wrapper--tabs">
<div class="wrapper wrapper--sidebar">
<article class="wrapper--sidebar--inner">
<h4 class="mb-2">{% trans "Project scope of work (SOW)" %}</h4>
<h4 class="mb-2">{% trans "Scope of Work" %}</h4>
<div class="card card--solid">
{% if object.sow.output_answers %}
<div class="rich-text rich-text--answers">
Expand All @@ -38,8 +38,12 @@ <h4 class="mb-2">{% trans "Project scope of work (SOW)" %}</h4>
<aside class="sidebar sidebar__project">
<div class="sidebar__inner sidebar__inner--light-blue sidebar__inner--actions">
<h5>{% trans "Actions to take" %}</h5>
<div x-data="{ show: false }" class="dropdown">
<button x-on:click="show = ! show" class="button button--primary" type="button">
{% user_can_edit_pfs object user as can_edit_pfs %}
{% if can_edit_pfs %}
<a class="button button--bottom-space button--primary button--full-width {% if user_can_approve %} is-disabled {% endif %}" href="{% url 'apply:projects:edit_sow' pk=object.pk %}">{% trans "Edit" %}</a>
{% endif %}
<div x-data="{ show: false }" class="relative">
<button x-on:click="show = ! show" class="button button--bottom-space button--primary button--full-width" type="button">
{% trans 'Download SOW' %}
</button>
<div x-show="show" x-transition class="dropdown__content">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{% extends "base-apply.html" %}
{% load i18n static %}
{% block title %}{% trans "Editing" %}: {{object.title }}{% endblock %}
{% block content %}

{% adminbar %}
{% slot back_link %}
<a class="simplified__projects-link" href="{{ object.get_absolute_url }}">
{% trans "View project page" %}
</a>
{% endslot %}
{% slot header %}{% trans "Editing" %}: {{ object.title }}{% endslot %}
{% endadminbar %}

{% if sow_form_exists %}
{% include "forms/includes/form_errors.html" with form=sow_form %}
<div class="pt-8 pb-8 mx-auto mt-4 mb-12 wrapper wrapper--default-bg wrapper--sidebar ps-20 ">
<div class="wrapper--sidebar--inner">
<form class="form application-form" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ sow_form.media }}
{% for field in sow_form %}
{% if field.field %}
{% if field.field.multi_input_field %}
{% include "forms/includes/multi_input_field.html" %}
{% else %}
{% include "forms/includes/field.html" %}
{% endif %}
{% else %}
{{ field.block }}
{% endif %}
{% endfor %}


{% for hidden_field in sow_form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
{% trans "Save draft" as save_draft %}
{% for button_name, button_type, button_value in buttons %}
<button class="button button--submit button--top-space button--{{ button_type }}" type="submit" name="{{ button_name }}" {% if button_value == save_draft %}formnovalidate{% endif %}>{{ button_value }}</button>
{% endfor %}
</form>
</div>
<aside class="sidebar sidebar__project">
{% if submissions_attachments %}
<div class="js-actions-sidebar sidebar__inner sidebar__inner--actions {% if mobile %}sidebar__inner--mobile{% endif %}">
<h5>{% trans "Proposal attachments" %}</h5>
{% for file in submissions_attachments %}
<p><b><a href="{{ file.url }}" target="_blank">{{ file.filename }}</a></b></p>
{% endfor %}
</div>
{% endif %}
</aside>
</div>
{% else %}
<div class="wrapper wrapper--default-bg wrapper--form wrapper--sidebar">
<div class="wrapper--sidebar--inner">
<p>
{% trans "Scope of work form not configured. Please add it in the" %}
<a href="{% url 'wagtailadmin_pages:edit' object.submission.page.id %}" target="_blank">{% trans "fund settings" %}</a>.
</p>
</div>
</div>
{% endif %}

{% endblock %}

{% block extra_js %}
<script src="{% static 'js/file-uploads.js' %}"></script>
<script src="{% static 'js/tinymce-word-count.js' %}"></script>
<script src="{% static 'js/multi-input-fields.js' %}"></script>
<script src="{% static 'js/application-form-links-new-window.js' %}"></script>
{% if not show_all_group_fields %}
<script src="{% static 'js/form-group-toggle.js' %}"></script>
{% endif %}
{% endblock %}
Loading

0 comments on commit 6ca172a

Please sign in to comment.