Skip to content

Commit

Permalink
FS-4954 - Update 'Build application' page to adapt to application status
Browse files Browse the repository at this point in the history
  • Loading branch information
wjrm500 committed Mar 6, 2025
1 parent 5c024d2 commit c3ede02
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 50 deletions.
24 changes: 23 additions & 1 deletion app/blueprints/application/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
)
from app.db.queries.clone import clone_single_form
from app.db.queries.fund import get_all_funds, get_fund_by_id
from app.db.queries.round import get_round_by_id
from app.db.queries.round import get_round_by_id, update_round
from app.export_config.generate_all_questions import generate_html
from app.export_config.generate_assessment_config import (
generate_assessment_config_for_round,
Expand Down Expand Up @@ -102,6 +102,28 @@ def build_application(round_id):
return render_template("build_application.html", round=round, fund=fund, back_link=back_link)


@application_bp.route("/<round_id>/mark-complete", methods=["GET"])
def mark_application_complete(round_id):
"""
Marks an application as complete
"""
round_ = get_round_by_id(round_id)
round_.status = "Complete"
update_round(round_)
return redirect(url_for("application_bp.build_application", round_id=round_id))


@application_bp.route("/<round_id>/mark-in-progress", methods=["GET"])
def mark_application_in_progress(round_id):
"""
Sets an application status back to 'In progress'
"""
round_ = get_round_by_id(round_id)
round_.status = "In progress"
update_round(round_)
return redirect(url_for("application_bp.build_application", round_id=round_id))


@application_bp.route("/<round_id>/sections/all-questions", methods=["GET"])
def view_all_questions(round_id):
"""
Expand Down
143 changes: 94 additions & 49 deletions app/blueprints/application/templates/build_application.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,24 @@
{% block content %}
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<span class="govuk-caption-l">Apply for {{ fund.title_json["en"] }}</span>
<h1 class="govuk-heading-l">Build application</h1>
<p class="govuk-body">Build the application by adding sections and using templates.</p>
<h1 class="govuk-heading-l">{% if round.status == 'Complete' %}View{% else %}Build{% endif %} application</h1>
{% if round.status == 'In progress' %}
<p class="govuk-body">Build the application by adding sections and using templates.</p>
{% endif %}
<p class="govuk-body">Application name: {{ fund.title_json["en"] }}</p>
<p class="govuk-body">Status:
<span class="govuk-tag {% if round.status == 'Complete' %}govuk-tag--green{% else %}govuk-tag--blue{% endif %}">
{{ round.status }}
</span>
</p>
</div>
{% if round.status == 'Complete' %}
<div class="govuk-grid-column-one-third govuk-!-text-align-right">
<a href="{{ url_for('application_bp.mark_application_in_progress', round_id=round.round_id) }}" class="govuk-button govuk-button--secondary" data-module="govuk-button">
Edit application
</a>
</div>
{% endif %}
</div>
<ul class="app-task-list__items">
{% for section in round.sections %}
Expand All @@ -19,64 +33,95 @@ <h1 class="govuk-heading-l">Build application</h1>
<h3 class="govuk-heading-m">{{ section.index }}. {{ section.name_in_apply_json["en"] }}</h3>
</span>
<span class="app-task-list__task-actions">
{% if section.index == round.sections | length %}
<span class="govuk-!-font-size-19 govuk-!-margin-right-2 disabled-link">Down</span>
{% else %}
<a class="govuk-!-font-size-19 govuk-!-margin-right-2 govuk-link--no-visited-state"
href='{{ url_for("application_bp.move_section_down_route",round_id=round.round_id, section_id=section.section_id) }}'>Down</a>
{% if round.status == 'In progress' %}
{% if section.index == round.sections | length %}
<span class="govuk-!-font-size-19 govuk-!-margin-right-2 disabled-link">Down</span>
{% else %}
<a class="govuk-!-font-size-19 govuk-!-margin-right-2 govuk-link--no-visited-state"
href='{{ url_for("application_bp.move_section_down_route",round_id=round.round_id, section_id=section.section_id) }}'>Down</a>
{% endif %}
{% if section.index == 1 %}
<span class="govuk-!-font-size-19 govuk-!-margin-right-2 disabled-link">Up</span>
{% else %}
<a class="govuk-!-font-size-19 govuk-!-margin-right-2 govuk-link--no-visited-state"
href='{{ url_for("application_bp.move_section_up_route",round_id=round.round_id, section_id=section.section_id) }}'>Up</a>
{% endif %}
<a class="govuk-button govuk-button--secondary govuk-!-margin-bottom-0"
href='{{ url_for("application_bp.section", round_id=round.round_id, section_id=section.section_id) }}'>Edit</a>
{% endif %}
{% if section.index == 1 %}
<span class="govuk-!-font-size-19 govuk-!-margin-right-2 disabled-link">Up</span>
{% else %}
<a class="govuk-!-font-size-19 govuk-!-margin-right-2 govuk-link--no-visited-state"
href='{{ url_for("application_bp.move_section_up_route",round_id=round.round_id, section_id=section.section_id) }}'>Up</a>
{% endif %}
<a class="govuk-button govuk-button--secondary govuk-!-margin-bottom-0"
href='{{ url_for("application_bp.section", round_id=round.round_id, section_id=section.section_id) }}'>Edit</a>
</span>
</li>
<li>
<ul class="app-task-list__items">
{% for form in section.forms %}
<li class="app-task-list__item task-list__new-design">
<span class="app-task-list__task-name">
<h3 class="govuk-body">
<a class="govuk-link govuk-link--no-visited-state"
target="_blank"
href='{{ url_for("index_bp.preview_form", form_id=form.form_id) }}'>
{{ form.name_in_apply_json["en"] }} (previews in a new tab)
</a>
</h3>
</span>
<span class="app-task-list__task-actions">
<a class="govuk-link--no-visited-state govuk-!-font-size-19" href="{{ url_for('application_bp.view_form_questions', round_id=round.round_id, section_id=section.section_id, form_id=form.form_id) }}">View questions</a>
</span>
</li>
{% endfor %}
</ul>
</li>
<ul class="app-task-list__items">
{% for form in section.forms %}
<li class="app-task-list__item task-list__new-design">
<span class="app-task-list__task-name">
<h3 class="govuk-body">
<a class="govuk-link govuk-link--no-visited-state"
target="_blank"
href='{{ url_for("index_bp.preview_form", form_id=form.form_id) }}'>
{{ form.name_in_apply_json["en"] }} (previews in a new tab)
</a>
</h3>
</span>
<span class="app-task-list__task-actions">
<a class="govuk-link--no-visited-state govuk-!-font-size-19" href="{{ url_for('application_bp.view_form_questions', round_id=round.round_id, section_id=section.section_id, form_id=form.form_id) }}">View questions</a>
</span>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
{{ govukButton({
"text": "Add section",
"href": url_for("application_bp.section", round_id=round.round_id),
"classes": "govuk-button--secondary"
})
}}
{% if round.status == 'In progress' %}
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
{% if round.status == 'In progress' %}
{{
govukButton({
"text": "Add section",
"href": url_for("application_bp.section", round_id=round.round_id),
"classes": "govuk-button--secondary"
})
}}
{% endif %}
</div>
<div class="govuk-grid-column-one-third govuk-!-text-align-right">
<p class="govuk-body">
<a class="govuk-link" href="{{ url_for('application_bp.view_all_questions', round_id=round.round_id) }}">View all application questions</a>
</p>
</div>
</div>
<div class="govuk-grid-column-one-third govuk-!-text-align-right">
<p class="govuk-body">
<a class="govuk-link" href="{{ url_for('application_bp.view_all_questions', round_id=round.round_id) }}">View all application questions</a>
</p>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<p class="govuk-body">
<a class="govuk-link" href="{{ url_for('application_bp.create_export_files', round_id=round.round_id) }}">Download application ZIP file</a>
</p>
</div>
</div>
</div>
{% else %}
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<p class="govuk-body">
<a class="govuk-link" href="{{ url_for('application_bp.create_export_files', round_id=round.round_id) }}">Download application ZIP file</a>
</p>
</div>
<div class="govuk-grid-column-one-third govuk-!-text-align-right">
<p class="govuk-body">
<a class="govuk-link" href="{{ url_for('application_bp.view_all_questions', round_id=round.round_id) }}">View all application questions</a>
</p>
</div>
</div>
{% endif %}
<div class="govuk-grid-row">
<div class="govuk-grid-column-three-quarters">
{% if round.status == 'In progress' %}
<p class="govuk-body">
<a class="govuk-link" href="{{ url_for('application_bp.create_export_files', round_id=round.round_id) }}">Download application ZIP file</a>
<a href="{{ url_for('application_bp.mark_application_complete', round_id=round.round_id) }}" class="govuk-button govuk-button--success" data-module="govuk-button">
Mark application complete
</a>
</p>
{% endif %}
</div>
</div>

Expand Down
88 changes: 88 additions & 0 deletions tests/unit/blueprints/application/test_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,91 @@ def test_update_template_form(flask_test_client, seed_dynamic_data):

# Assert that the search text is found in at least one <h3> tag
assert found, "Template Form not found"


@pytest.mark.usefixtures("set_auth_cookie", "patch_validate_token_rs256_internal_user")
def test_mark_application_complete(flask_test_client, seed_dynamic_data):
"""Test marking an application as complete"""
test_round = seed_dynamic_data["rounds"][0]

# Ensure the round starts with "In progress" status
assert test_round.status == "In progress"

# Call the endpoint to mark application as complete
url = f"/rounds/{test_round.round_id}/mark-complete"
response = flask_test_client.get(url, follow_redirects=True)
assert response.status_code == 200

# Check the page content reflects the completed status
soup = BeautifulSoup(response.data, "html.parser")

# Title should be "View application"
assert soup.find("h1", class_="govuk-heading-l").text.strip() == "View application"

# Status tag should be green
status_tag = soup.find("span", class_="govuk-tag")
assert "govuk-tag--green" in status_tag["class"]
assert status_tag.text.strip() == "Complete"

# "Edit application" button should be visible
edit_button = soup.find("a", string=lambda text: "Edit application" in text if text else False)
assert edit_button is not None

# "Mark application complete" button should NOT be visible
complete_button = soup.find("a", string=lambda text: "Mark application complete" in text if text else False)
assert complete_button is None

# "Add section" button should NOT be visible
add_section_button = soup.find("a", string=lambda text: "Add section" in text if text else False)
assert add_section_button is None

# "Edit", "Up", "Down" buttons for sections should NOT be visible
edit_buttons = soup.find_all("a", string="Edit")
assert len(edit_buttons) == 0
up_links = soup.find_all("a", string="Up")
assert len(up_links) == 0
down_links = soup.find_all("a", string="Down")
assert len(down_links) == 0


@pytest.mark.usefixtures("set_auth_cookie", "patch_validate_token_rs256_internal_user")
def test_mark_application_in_progress(flask_test_client, seed_dynamic_data):
"""Test marking a complete application as in progress"""
test_round = seed_dynamic_data["rounds"][0]

# First mark the application as complete
flask_test_client.get(f"/rounds/{test_round.round_id}/mark-complete")

# Then mark it back as in progress
url = f"/rounds/{test_round.round_id}/mark-in-progress"
response = flask_test_client.get(url, follow_redirects=True)
assert response.status_code == 200

# Check the page content reflects the in-progress status
soup = BeautifulSoup(response.data, "html.parser")

# Title should be "Build application"
assert soup.find("h1", class_="govuk-heading-l").text.strip() == "Build application"

# Status tag should be blue
status_tag = soup.find("span", class_="govuk-tag")
assert "govuk-tag--blue" in status_tag["class"]
assert status_tag.text.strip() == "In progress"

# "Edit application" button should NOT be visible
edit_button = soup.find("a", string=lambda text: "Edit application" in text if text else False)
assert edit_button is None

# "Mark application complete" button should be visible
complete_button = soup.find("a", string=lambda text: "Mark application complete" in text if text else False)
assert complete_button is not None

# "Add section" button should be visible
add_section_button = soup.find("a", string=lambda text: "Add section" in text if text else False)
assert add_section_button is not None

# If there are sections, "Edit", "Up", "Down" buttons should be visible
# We need to create a section first to test this
if len(seed_dynamic_data["rounds"][0].sections) > 0:
edit_buttons = soup.find_all("a", string="Edit")
assert len(edit_buttons) > 0
3 changes: 3 additions & 0 deletions tests/unit/seed_test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"is_research_survey_optional": False,
},
"eligibility_config": {"has_eligibility": False},
"status": "In progress",
}

page_one_id = uuid4()
Expand Down Expand Up @@ -410,6 +411,7 @@ def init_unit_test_data() -> dict:
},
eligibility_config={"has_eligibility": False},
eoi_decision_schema={"en": {"valid": True}, "cy": {"valid": False}},
status="In progress",
)

s1: Section = Section(
Expand Down Expand Up @@ -537,6 +539,7 @@ def fund_without_assessment() -> dict:
},
eligibility_config={"has_eligibility": False},
eoi_decision_schema={"en": {"valid": True}, "cy": {"valid": False}},
status="In progress",
)

f2_r1_s1: Section = Section(
Expand Down

0 comments on commit c3ede02

Please sign in to comment.