Skip to content

Commit

Permalink
Add budget component (#1979)
Browse files Browse the repository at this point in the history
* feat(remaining-messages-summary): add new component

* feat(storybook): add a lightweight "storybook" route so we can test components more easily

* task: add testids

* feat(storybook): add storybooks for components

* test(rms): add suite of tests; add tests to ci

* chore: formatting

* a11y(rms): add icon labels [review]

* a11y(storybook): fix up heading issues

* chore: add translation

* ai [review] plz

* chore: remove unnecessary code

* chore(tests): fix test descriptions

* Please [review] again

* task: remove content from styleguide that was moved to a storybook

* task: remove testing code

* Update app/templates/components/remaining-messages-summary.html

* Update app/templates/views/storybook/remaining-messages-summary.html

* fix: undo AI suggestion as it errors

* fix(storybook_menu): move component array; use ul/li

* import faCircleCheck, move icon out of the summary div, add some wrapping, adjust spaing and alignments

* chore: regen css

* fix: update link for usage reports in the component

---------

Co-authored-by: Jumana B <[email protected]>
Co-authored-by: Philippe Caron <[email protected]>
  • Loading branch information
3 people authored Nov 7, 2024
1 parent 40f91d7 commit 35b037d
Show file tree
Hide file tree
Showing 15 changed files with 575 additions and 4 deletions.
1 change: 1 addition & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ def get_locale():
application.jinja_env.globals["show_tou_prompt"] = show_tou_prompt
application.jinja_env.globals["parse_ua"] = parse
application.jinja_env.globals["events_key"] = EVENTS_KEY
application.jinja_env.globals["now"] = datetime.utcnow

# Initialize Salesforce Account list
if application.config["FF_SALESFORCE_CONTACT"]:
Expand Down
2 changes: 2 additions & 0 deletions app/assets/javascripts/fontawesome.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { faAngleDown } from "@fortawesome/free-solid-svg-icons/faAngleDown";
import { faCircleQuestion } from "@fortawesome/free-solid-svg-icons/faCircleQuestion";
import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons/faTriangleExclamation";
import { faCircleExclamation } from "@fortawesome/free-solid-svg-icons/faCircleExclamation";
import { faCircleCheck } from "@fortawesome/free-solid-svg-icons/faCircleCheck";
import { faInfoCircle } from "@fortawesome/free-solid-svg-icons";
import { faXmark } from "@fortawesome/free-solid-svg-icons/faXMark";
import { faTag } from "@fortawesome/free-solid-svg-icons/faTag";
Expand All @@ -29,6 +30,7 @@ let FontAwesomeIconLoader = () => {
faCircleQuestion,
faTriangleExclamation,
faCircleExclamation,
faCircleCheck,
faInfoCircle,
faXmark,
faTag,
Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/main.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion app/assets/stylesheets/index.css

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions app/main/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
set_lang,
sign_in,
sign_out,
storybook,
styleguide,
templates,
two_factor,
Expand Down
12 changes: 12 additions & 0 deletions app/main/views/storybook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from flask import render_template, request

from app.main import main


@main.route("/_storybook")
def storybook():
component = None
if "component" in request.args:
component = request.args["component"]

return render_template("views/storybook.html", component=component)
108 changes: 108 additions & 0 deletions app/templates/components/remaining-messages-summary.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
{% macro remaining_messages_summary(dailyLimit, dailyUsed, yearlyLimit, yearlyUsed, notification_type, textOnly=None) %}
<!-- Validate textOnly param -->
{% set textOnly_allowed_values = ['text', 'emoji'] %}
{% if textOnly not in textOnly_allowed_values %}
{% set textOnly = None %}
{% set textOnlyMode = false %}
{% else %}
{% set textOnlyMode = true %}
{% endif %}

<!-- Validate notification_type param -->
{% set notificationType_allowed_values = ['sms', 'email'] %}
{% if notification_type not in notificationType_allowed_values %}
{% set notification_type = None %}
{% if config["NOTIFY_ENVIRONMENT"].lower() == 'development' %}
<p class="text-red-300 mt-6 mb-6"><i aria-hidden="true" class="fa-solid fa-fas fa-triangle-exclamation text-yellow w-8 h-8 text-xs rounded-full mx-1" data-testid="rms-icon"></i> Invalid notification type - check your jinja template!</p>
{% endif %}
{% else %}
{% if notification_type == 'sms' %}
{% set notification_type = _('text messages') %}
{% elif notification_type == 'email' %}
{% set notification_type = _('emails') %}
{% endif %}
{% endif %}

<!-- Set some constants -->
{% set current_year = current_year or (now().year if now().month < 4 else now().year + 1) %}
{% set next_april = _('April 1, ') ~ current_year %}
{% set icon_default = 'fa-circle-check text-blue-300 text-base self-start' %}
{% set icon_warning = 'fa-circle-exclamation text-red-300 text-base self-start' %}
{% set warning_threshold = 0.8 %}

{% set sections = [
{
'type': 'daily',
'used': dailyUsed,
'limit': dailyLimit,
'text': _('remaining until') ~ ' ' ~ ('7 pm ET'),
'link_text': _('Request a daily limit increase'),
'link_href': url_for('main.contact'),
'remaining': "{:,}".format(dailyLimit - dailyUsed) if session['userlang'] == 'en' else "{:,}".format(dailyLimit - dailyUsed).replace(',', ' '),
'skip': false if textOnlyMode else dailyLimit - dailyUsed == 0 and yearlyLimit - yearlyUsed == 0,
'warn': dailyUsed / dailyLimit >= warning_threshold
},
{
'type': 'yearly',
'used': yearlyUsed,
'limit': yearlyLimit,
'text': _('remaining until') ~ ' ' ~ next_april,
'link_text': _('Visit Usage reports'),
'link_href': url_for('.monthly', service_id=current_service.id),
'remaining': "{:,}".format(yearlyLimit - yearlyUsed) if session['userlang'] == 'en' else "{:,}".format(yearlyLimit - yearlyUsed).replace(',', ' '),
'warn': yearlyUsed / yearlyLimit >= warning_threshold
}
] %}

{% if textOnlyMode %}
<div class="mt-4 pl-10 py-2 border-l-8 border-gray-300">
{% endif %}
<div data-testid="rms">
{% for section in sections if not section.skip %}
{% if textOnly == None %}
<div class="flex items-baseline border-b border-gray-300 py-4 gap-4 text-small " data-testid="rms-item">
{% set icon_class = icon_default %}
{% set icon_type = "default" %}
{% if section.warn %}
{% set icon_class = icon_warning %}
{% set icon_type = "warning" %}
{% endif %}
<span data-testid="rms-icon-{{ icon_type }}"></span>
<i aria-hidden="true" class="fa-solid fa-fas {{ icon_class }}" {{ 'aria-label=' ~ _('warning') if icon_type=="warning"
else "" }}></i>
<div class="flex flex-wrap items-baseline gap-x-2">
<span class="font-bold" data-testid="rms-{{ section.type }}-remaining">{{ section.remaining }}</span> {{ section.text
}}
</div>
<a href="{{ section.link_href }}" data-testid="rms-{{ section.type }}-link" class="ml-auto text-blue-500">{{
section.link_text }}</a>
</div>
{% else %}
<p class="m-0 p-0" data-testid="rms-item">
{% if section.remaining == "0" %}
<span data-testid="text-prefix-at-limit">{{ _('At limit: ') if textOnly == 'text' else '⚠️' }}</span>
{% elif section.warn %}
<span data-testid="text-prefix-near-limit">{{ _('Near limit: ') if textOnly == 'text' else '⚠️' }}</span>
{% else %}
<span data-testid="text-prefix-below-limit">{{ _('Below limit: ') if textOnly == 'text' else '🆗' }}</span>
{% endif %}
{{ section.remaining }} {{notification_type}} {{ section.text }}
</p>
{% endif %}
{% endfor %}
</div>
{% if textOnlyMode %}
</div>
{% endif %}

{% if sections[0].skip %}
<p class="mt-4 pl-10 py-4 border-l-4 border-gray-300" data-testid="yearly-sending-paused">
Sending paused until annual limit resets
</p>
{% elif sections[0].remaining == "0" %}
<p class="mt-4 pl-10 py-4 border-l-4 border-gray-300" data-testid="daily-sending-paused">
Sending paused until 7pm ET. You can schedule more messages to send later.
</p>
{% endif %}

{% endmacro %}
29 changes: 29 additions & 0 deletions app/templates/views/storybook.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{% extends "admin_template.html" %}
{% from 'views/storybook/storybook-menu.html' import storybook_menu %}

{% block page_title %} Storybook {% endblock %}

{% block maincolumn_content %}
<style>
.container {
max-width: 1536px;
}
</style>

<div class="flex">
<!-- Left Column -->
<div class="w-1/4 p-4 bg-gray-100">
<h1 class="text-lg font-bold mb-4">Components</h1>
{{ storybook_menu() }}
</div>

<!-- Main Content Area -->
<div class="w-3/4 p-4">
{% if component %}
{% include 'views/storybook/' ~ component ~ '.html' %}
{% else %}
<p>Choose a component</p>
{% endif %}
</div>

{% endblock %}
85 changes: 85 additions & 0 deletions app/templates/views/storybook/remaining-messages-summary.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{% from 'components/remaining-messages-summary.html' import remaining_messages_summary with context %}

<h2><code>remaining_messages_summary</code></h2>
<hr />
<h3 class="heading-medium">Below limits</h3>
<div class="grid grid-cols-2 gap-6 bg-gray p-6" data-testid="rms-below">

<div class="mb-6 bg-white p-4">
{{ remaining_messages_summary(1000, 799, 1000, 799, "email") }}
</div>
<div class="mb-6 bg-white p-4">
{{ remaining_messages_summary(10000, 699, 10000, 699, "sms") }}
</div>
<div class="mb-6 bg-white p-4">
{{ remaining_messages_summary(1000, 0, 1000, 0, "email") }}
</div>
</div>

<h3 class="heading-medium">Warning</h3>
<div class="grid grid-cols-2 gap-6 bg-gray p-6" data-testid="rms-warning">
<div class="mb-6 bg-white p-4">
{{ remaining_messages_summary(1000, 800, 1000, 800, "email") }}
</div>
<div class="mb-6 bg-white p-4">
{{ remaining_messages_summary(1000, 900, 1000, 900, "email") }}
</div>
</div>

<h3 class="heading-medium">At limit</h3>
<div class="grid grid-cols-2 gap-6 bg-gray p-6" data-testid="rms-limit-both">
At both limits
<div class="mb-6 bg-white p-4">
{{ remaining_messages_summary(1000, 1000, 1000, 1000, "email") }}
</div>
</div>
<div class="grid grid-cols-2 gap-6 bg-gray p-6" data-testid="rms-limit-daily">
At daily limit
<div class="mb-6 bg-white p-4">
{{ remaining_messages_summary(1000, 1000, 1000, 500, "email") }}
</div>
</div>

<h3 class="heading-medium">Mixed</h3>
<div class="grid grid-cols-2 gap-6 bg-gray p-6" data-testid="rms-mixed">
<div class="mb-6 bg-white p-4">
{{ remaining_messages_summary(1000, 100, 1000, 1000, "email") }}
</div>
<div class="mb-6 bg-white p-4">
{{ remaining_messages_summary(1000, 1000, 1000, 100, "email") }}
</div>
</div>

<h3 class="heading-medium">Text only</h3>
<div class="grid grid-cols-2 gap-6 bg-gray p-6" data-testid="rms-text-below">below limit
<div class="mb-6 bg-white p-4">
{{ remaining_messages_summary(10000, 700, 10000, 750, "email", "text") }}
</div>
</div>
<div class="grid grid-cols-2 gap-6 bg-gray p-6" data-testid="rms-text-warning">near limit
<div class="mb-6 bg-white p-4">
{{ remaining_messages_summary(1000, 800, 1000, 900, "email", "text") }}
</div>
</div>
<div class="grid grid-cols-2 gap-6 bg-gray p-6" data-testid="rms-text-limit">at limit
<div class="mb-6 bg-white p-4">
{{ remaining_messages_summary(1000, 1000, 1000, 1000, "email", "text") }}
</div>
</div>

<h3 class="heading-medium">Text only emoji</h3>
<div class="grid grid-cols-2 gap-6 bg-gray p-6" data-testid="rms-emoji-below">below limit
<div class="mb-6 bg-white p-4">
{{ remaining_messages_summary(1000, 700, 1000, 750, "email", "emoji") }}
</div>
</div>
<div class="grid grid-cols-2 gap-6 bg-gray p-6" data-testid="rms-emoji-warning">near limit
<div class="mb-6 bg-white p-4">
{{ remaining_messages_summary(1000, 800, 1000, 900, "email", "emoji") }}
</div>
</div>
<div class="grid grid-cols-2 gap-6 bg-gray p-6" data-testid="rms-emoji-limit">at limit
<div class="mb-6 bg-white p-4">
{{ remaining_messages_summary(1000, 1000, 1000, 1000, "email", "emoji") }}
</div>
</div>
46 changes: 46 additions & 0 deletions app/templates/views/storybook/remaining-messages.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{% from 'components/remaining-messages.html' import remaining_messages with context %}

<h1 class="heading-large">remaining_messages</h1>
<hr />
<h2 class="heading-medium">Below limits</h2>
<div class="grid grid-cols-2 gap-4" data-testid="rm-below">
{{ remaining_messages(header=_('emails'), total=1000, used=1) }}
{{ remaining_messages(header=_('emails'), total=1000, used=250) }}
{{ remaining_messages(header=_('emails'), total=1000, used=500) }}
{{ remaining_messages(header=_('emails'), total=1000, used=799) }}
</div>

<h2 class="heading-medium">Warning</h2>
<div class="grid grid-cols-2 gap-4" data-testid="rm-warning">
{{ remaining_messages(header=_('emails'), total=1000, used=800) }}
{{ remaining_messages(header=_('emails'), total=1000, used=825) }}
{{ remaining_messages(header=_('emails'), total=1000, used=900) }}
{{ remaining_messages(header=_('emails'), total=1000, used=999) }}
</div>

<h2 class="heading-medium">At limit</h2>
<div class="grid grid-cols-2 gap-4" data-testid="rm-limit">
{{ remaining_messages(header=_('emails'), total=1000, used=1000) }}
</div>


<h2 class="heading-medium">Muted - Below limits</h2>
<div class="grid grid-cols-2 gap-4" data-testid="rm-muted-below">
{{ remaining_messages(header=_('emails'), total=1000, used=1, muted=true) }}
{{ remaining_messages(header=_('emails'), total=1000, used=250, muted=true) }}
{{ remaining_messages(header=_('emails'), total=1000, used=500, muted=true) }}
{{ remaining_messages(header=_('emails'), total=1000, used=799, muted=true) }}
</div>

<h2 class="heading-medium">Muted - Warning</h2>
<div class="grid grid-cols-2 gap-4" data-testid="rm-muted-warning">
{{ remaining_messages(header=_('emails'), total=1000, used=800, muted=true) }}
{{ remaining_messages(header=_('emails'), total=1000, used=825, muted=true) }}
{{ remaining_messages(header=_('emails'), total=1000, used=900, muted=true) }}
{{ remaining_messages(header=_('emails'), total=1000, used=999, muted=true) }}
</div>

<h2 class="heading-medium">Muted - At limit</h2>
<div class="grid grid-cols-2 gap-4" data-testid="rm-muted-limit">
{{ remaining_messages(header=_('emails'), total=1000, used=1000, muted=true) }}
</div>
21 changes: 21 additions & 0 deletions app/templates/views/storybook/storybook-menu.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!-- macro to display the component menu -->

{% macro storybook_menu() %}
{% set components = [
{
'name': 'Remaining Messages Summary',
'path': 'remaining-messages-summary'
},
{
'name': 'Remaining Messages',
'path': 'remaining-messages'
},
] %}
<ul class="list list-bullet ml-10">
<!-- loop through components -->
{% for component in components %}
<li><a href="{{ url_for('main.storybook', component=component.path) }}"
class="text-blue-500 hover:underline">{{ component.name }}</a></li>
{% endfor %}
</ul>
{% endmacro %}
3 changes: 1 addition & 2 deletions app/templates/views/styleguide.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"components/api-key.html" import api_key %} {% from
"components/template-filter.html" import template_filter with context %} {%
block per_page_title %} Styleguide {% endblock %} {% block maincolumn_content %}

{% from 'components/remaining-messages-summary.html' import remaining_messages_summary with context %}
<h2 class="heading-large">Banner</h2>
<div class="grid-row contain-floats">
<div class="md:w-3/4 float-left py-0 px-0 px-gutterHalf box-border">
Expand Down Expand Up @@ -388,5 +388,4 @@ <h2 class="heading-medium">Browse Templates</h2>
</fieldset>
</nav>
<hr />

{% endblock %}
5 changes: 5 additions & 0 deletions app/translations/csv/fr.csv
Original file line number Diff line number Diff line change
Expand Up @@ -1987,11 +1987,16 @@
"The service {} responded in {} seconds.","Le service {} a répondu en {} secondes."
"Are you sure you want to delete this callback configuration?","Voulez-vous vraiment supprimer cette configuration de rappel?"
"Your Callback configuration has been deleted.","Votre configuration de rappel a été supprimée."
"April 1, ","1er avril "
"remaining until","restant jusqu'à"
"Request a daily limit increase","Demander une augmentation de limite quotidienne"
"Visit Usage reports","Consultez les rapports d'utilisation"
"The service {} took longer than 1 second to respond.","Le service {} a mis plus d’une seconde à répondre. Votre service doit répondre dans un délai d’une seconde pour permettre le bon fonctionnement de Notification GC."
"Display","Afficher"
"email content","le message du courriel"
"Email content","Message du courriel"
"right to left","de droite à gauche"
"warning","avertissement"
"Annual text message limit","(FR) Limite maximale de messages texte par exercice financier"
"Annual email message limit","(FR) Limite maximale de messages électroniques par exercice financier"
"Annual email limit","(FR) Limite maximale de courriels par exercice financier"
1 change: 1 addition & 0 deletions tests_cypress/cypress/e2e/admin/ci.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ import "./template-categories.cy";
import "./template/create-template.cy";
import "./template/edit-template.cy";
import "./template/text-direction.cy";
import "./components/remaining_messages_summary.cy";
Loading

0 comments on commit 35b037d

Please sign in to comment.