Merge pull request #574 from dod-ccpo/edit-to-invitations
Add page that shows status of TO officer invitations
This commit is contained in:
commit
eff32852fb
@ -61,3 +61,16 @@ def view_task_order(portfolio_id, task_order_id):
|
|||||||
return render_template(
|
return render_template(
|
||||||
"portfolios/task_orders/show.html", portfolio=portfolio, task_order=task_order
|
"portfolios/task_orders/show.html", portfolio=portfolio, task_order=task_order
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@portfolios_bp.route(
|
||||||
|
"/portfolios/<portfolio_id>/task_order/<task_order_id>/invitations"
|
||||||
|
)
|
||||||
|
def task_order_invitations(portfolio_id, task_order_id):
|
||||||
|
portfolio = Portfolios.get(g.current_user, portfolio_id)
|
||||||
|
task_order = TaskOrders.get(g.current_user, task_order_id)
|
||||||
|
return render_template(
|
||||||
|
"portfolios/task_orders/invitations.html",
|
||||||
|
portfolio=portfolio,
|
||||||
|
task_order=task_order,
|
||||||
|
)
|
||||||
|
@ -267,3 +267,84 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.task-order-invitations {
|
||||||
|
.task-order-invitations__heading {
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
&.subheading .h2 {
|
||||||
|
color: $color-gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.officer {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: $gap;
|
||||||
|
padding-bottom: 4 * $gap;
|
||||||
|
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
border-bottom-color: $color-gray-light;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
color: $color-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.officer__info {
|
||||||
|
.officer__info--name {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: .5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.officer__info--name {
|
||||||
|
margin-right: 2 * $gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.officer__info--status {
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&.invited {
|
||||||
|
color: $color-green;
|
||||||
|
.icon {
|
||||||
|
@include icon-color($color-green);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.uninvited {
|
||||||
|
color: $color-red;
|
||||||
|
.icon {
|
||||||
|
@include icon-color($color-red);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.officer__actions {
|
||||||
|
margin-left: -2 * $gap;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-left: 2 * $gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-link {
|
||||||
|
margin: 0 $gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove {
|
||||||
|
color: $color-red;
|
||||||
|
.icon {
|
||||||
|
@include icon-color($color-red);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
94
templates/portfolios/task_orders/invitations.html
Normal file
94
templates/portfolios/task_orders/invitations.html
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
{% extends "portfolios/base.html" %}
|
||||||
|
|
||||||
|
{% from "components/icon.html" import Icon %}
|
||||||
|
|
||||||
|
{% macro Link(text, icon_name, url='#', classes='') %}
|
||||||
|
<a href="{{ url }}" class="icon-link {{ classes }}">
|
||||||
|
{{ Icon(icon_name) }}
|
||||||
|
<span>{{ text }}</span>
|
||||||
|
</a>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro OfficerInfo(task_order, officer_type) %}
|
||||||
|
<div class="panel__content officer">
|
||||||
|
<h2 class="officer__title">{{ ("task_orders.invitations." + officer_type + ".title") | translate }}</h2>
|
||||||
|
<p class="officer__description">{{ ("task_orders.invitations." + officer_type + ".description") | translate }}</p>
|
||||||
|
|
||||||
|
{% set prefix = { "contracting_officer": "ko", "contracting_officer_representative": "cor", "security_officer": "so" }[officer_type] %}
|
||||||
|
{% set first_name = task_order[prefix + "_first_name"] %}
|
||||||
|
{% set last_name = task_order[prefix + "_last_name"] %}
|
||||||
|
{% set email = task_order[prefix + "_email"] %}
|
||||||
|
{% set phone_number = task_order[prefix + "_phone_number"] %}
|
||||||
|
{% set dod_id = task_order[prefix + "_dod_id"] %}
|
||||||
|
|
||||||
|
{% if task_order[officer_type] %}
|
||||||
|
<div class="officer__info">
|
||||||
|
<div class="row">
|
||||||
|
<div class="officer__info--name">{{ first_name }} {{ last_name }}</div>
|
||||||
|
<div class="officer__info--status invited">
|
||||||
|
<span>{{ Icon("ok", classes="invited") }}</span>
|
||||||
|
<span>Invited</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="officer__info--email">{{ email }}</p>
|
||||||
|
<p class="officer__info--phone">{{ phone_number | usPhone }}</p>
|
||||||
|
<p class="officer__info--dod_id">{{ "task_orders.invitations.dod_id_label" | translate}}: {{ dod_id }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="officer__actions">
|
||||||
|
{{ Link("Update", "edit") }}
|
||||||
|
{{ Link("Resend Invitation", "avatar") }}
|
||||||
|
{{ Link("Remove", "trash", classes="remove") }}
|
||||||
|
</div>
|
||||||
|
{% elif first_name and last_name %}
|
||||||
|
<div class="officer__info">
|
||||||
|
<div class="row">
|
||||||
|
<div class="officer__info--name">{{ first_name }} {{ last_name }}</div>
|
||||||
|
<div class="officer__info--status uninvited">
|
||||||
|
<span>{{ Icon("alert", classes="uninvited") }}</span>
|
||||||
|
Not Invited
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="officer__info--email">{{ email }}</p>
|
||||||
|
<p class="officer__info--phone">{{ phone_number | usPhone }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="officer__actions">
|
||||||
|
{{ Link("Update", "edit") }}
|
||||||
|
{{ Link("Remove", "trash", classes="remove") }}
|
||||||
|
<button type='button' class='usa-button usa-button-primary'>
|
||||||
|
{{ ("task_orders.invitations." + officer_type + ".invite_button_text") | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="officer__info">
|
||||||
|
<div class="officer__info--status uninvited">
|
||||||
|
<span>{{ Icon("alert", classes="uninvited") }}</span>
|
||||||
|
Not specified
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="officer__actions">
|
||||||
|
<button type='button' class='usa-button usa-button-primary'>
|
||||||
|
{{ ("task_orders.invitations." + officer_type + ".add_button_text") | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% block portfolio_content %}
|
||||||
|
<div class="task-order-invitations">
|
||||||
|
{% include "fragments/flash.html" %}
|
||||||
|
|
||||||
|
<div class="panel">
|
||||||
|
<div class="panel__heading">
|
||||||
|
<h1 class="task-order-invitations__heading subheading">
|
||||||
|
<div class="h2">Edit Task Order</div>
|
||||||
|
Oversight
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% for officer in ["contracting_officer", "contracting_officer_representative", "security_officer"] %}
|
||||||
|
{{ OfficerInfo(task_order, officer) }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -153,7 +153,7 @@
|
|||||||
{{ InvitationStatus('Contracting Officer Representative', task_order.contracting_officer_representative) }}
|
{{ InvitationStatus('Contracting Officer Representative', task_order.contracting_officer_representative) }}
|
||||||
{{ InvitationStatus('IA Security Officer', officer=task_order.security_officer) }}
|
{{ InvitationStatus('IA Security Officer', officer=task_order.security_officer) }}
|
||||||
|
|
||||||
<a href="{{ url_for('task_orders.new', screen=3, task_order_id=task_order.id) }}" class="icon-link">
|
<a href="{{ url_for('portfolios.task_order_invitations', portfolio_id=portfolio.id, task_order_id=task_order.id) }}" class="icon-link">
|
||||||
{{ Icon("edit") }}
|
{{ Icon("edit") }}
|
||||||
<span>manage invitations</span>
|
<span>manage invitations</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -86,3 +86,17 @@ def test_ko_can_view_task_order(client, user_session):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_view_task_order_invitations(client, user_session):
|
||||||
|
portfolio = PortfolioFactory.create()
|
||||||
|
user_session(portfolio.owner)
|
||||||
|
task_order = TaskOrderFactory.create(portfolio=portfolio)
|
||||||
|
response = client.get(
|
||||||
|
url_for(
|
||||||
|
"portfolios.task_order_invitations",
|
||||||
|
portfolio_id=portfolio.id,
|
||||||
|
task_order_id=task_order.id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
@ -438,6 +438,23 @@ task_orders:
|
|||||||
invited: Invited
|
invited: Invited
|
||||||
not_invited: Not Yet Invited
|
not_invited: Not Yet Invited
|
||||||
not_uploaded: Not Uploaded
|
not_uploaded: Not Uploaded
|
||||||
|
invitations:
|
||||||
|
dod_id_label: DoD ID
|
||||||
|
contracting_officer:
|
||||||
|
title: Contracting Officer (KO) Information
|
||||||
|
description: You'll need a signature from your KO. You might want to work with your program Financial Manager to get your TO documents moved in the right direction.
|
||||||
|
add_button_text: Add / Invite KO
|
||||||
|
invite_button_text: Invite KO
|
||||||
|
contracting_officer_representative:
|
||||||
|
title: Contracting Officer Representative (COR) Information
|
||||||
|
description: Your COR may assist in submitting the Task Order documents within their official system of record.
|
||||||
|
add_button_text: Add / Invite COR
|
||||||
|
invite_button_text: Invite COR
|
||||||
|
security_officer:
|
||||||
|
title: IA Security Officer Information
|
||||||
|
description: Your Security Officer will need to answer some security configuration questions in order to generate a DD-254 document, then electronically sign.
|
||||||
|
add_button_text: Add / Invite Security Officer
|
||||||
|
invite_button_text: Invite Security Officer
|
||||||
testing:
|
testing:
|
||||||
example_string: Hello World
|
example_string: Hello World
|
||||||
example_with_variables: 'Hello, {name}!'
|
example_with_variables: 'Hello, {name}!'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user