Repository: cal-itp/benefits

Issue:
`Enrollment index`: Update for CalFresh
This page will be substantially different from other flows for CalFresh eligibility. 

*Full page*

![image](https://github.com/cal-itp/benefits/assets/1783439/c6cc3277-17c5-4377-8a30-51d7c45726df)

*Informational Alert component*

![image](https://github.com/cal-itp/benefits/assets/1783439/a62be9cc-e4da-4adc-9df0-c69c7da72921)

## Figma links

* [`Enrollment index` page (Desktop)](https://www.figma.com/file/SeSd3LaLd6WkbEYhmtKpO3/Benefits-(Full-Application)?type=design&node-id=13737-11712&mode=design&t=7Gb5yK9MsYWUi5pt-4)
* [`Enrollment index` page (Mobile)](https://www.figma.com/file/SeSd3LaLd6WkbEYhmtKpO3/Benefits-(Full-Application)?type=design&node-id=13861-2753&mode=design&t=7Gb5yK9MsYWUi5pt-4)
* [Information Alert component](https://www.figma.com/file/IuVNsLccmvumsUzW7IC5BF/Cal-ITP-Benefits-Component-Library?type=design&node-id=493-427&mode=design&t=86GNjG59Gdbmao1Y-4)

## Acceptance Criteria

- [x] Headline and first paragraph remains the same.
- [x] Remove the “Please use a debit and credit card by Visa or Mastercard.” paragraph that is in the other flows’ screens.
- [x] New component - Alert box (Informational alert), links to the same CalFresh modal from #1918
  - [x] Space between bottom of media item and top of alert box: `48px`
  - [x] Space between bottom of alert box and call to action button: `64px`
  - [x] Bottom margin of alert box heading: `4px`
  - [x] Background yellow: `#FFF1D0`
  - [x] Border yellow: `#FDB714`
  - [x] Link: `#045B86`
  - [x] Link focus ring: `#045B86`
  - [x] Border width: `8px`
  - [x] Left padding from border to text: `16px`
  - [x] Right padding from text to edge: `16px`
  - [x] Top/bottom padding from text to edge: `16px`



Discussion:
Starting to get into this and one immediate question that comes up: 

**How to override the `enrollment:index` template for CalFresh?**

## Background

We typically override templates by storing the name of the template to use on a particular model. Today we have:

* `AuthProvider` stores its `sign_out_button_template` and `sign_out_link_template` for defining those UI components
* `EligibilityVerifier` stores the `selection_label_template` and `start_template` for defining the eligibility phase
* `TransitAgency` stores an `index_template` and `help_template` for its own homepage and help page, an `eligibility_index_template` for the beginning of its eligibility phase, and an `enrollment_success_template` for the end of its enrollment phase

## Options

I can see 3 options for how to override the `enrollment:index` template:

* `PaymentProcessor`: I actually don't think this will work, since each agency only has a single payment processor instance, and we need this template to be specific to the flow -- including for posterity
* `EligibilityVerifier`: this would work in the sense that we typically create a verifier per flow, but it sort of feels wrong though? Since we're talking about enrollment specifically, which is entirely outside of the eligibility phase
* `EligibilityType`: of the options, this is the one that makes the most sense to me. We already store some enrollment information here (e.g. `group_id` and the `expiry` fields), and of course this is the most specific in terms of the flow, i.e. we always have a new instance for each flow. 

@angela-tran am I missing any options here? Any thoughts on the above options? Can you think of another way we might want to override this template that doesn't involve a model field?

Resolving patch:
diff --git a/benefits/core/migrations/0007_eligibilitytype_enrollment_index_template.py b/benefits/core/migrations/0007_eligibilitytype_enrollment_index_template.py
new file mode 100644
--- /dev/null
+++ b/benefits/core/migrations/0007_eligibilitytype_enrollment_index_template.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.0.3 on 2024-03-25 22:50
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("core", "0006_alter_allow_blank"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="eligibilitytype",
+            name="enrollment_index_template",
+            field=models.TextField(default="enrollment/index.html"),
+        ),
+    ]
diff --git a/benefits/core/models.py b/benefits/core/models.py
--- a/benefits/core/models.py
+++ b/benefits/core/models.py
@@ -113,6 +113,7 @@ class EligibilityType(models.Model):
     supports_expiration = models.BooleanField(default=False)
     expiration_days = models.PositiveSmallIntegerField(null=True, blank=True)
     expiration_reenrollment_days = models.PositiveSmallIntegerField(null=True, blank=True)
+    enrollment_index_template = models.TextField(default="enrollment/index.html")
 
     def __str__(self):
         return self.label
diff --git a/benefits/enrollment/views.py b/benefits/enrollment/views.py
--- a/benefits/enrollment/views.py
+++ b/benefits/enrollment/views.py
@@ -11,7 +11,7 @@
 from littlepay.api.client import Client
 from requests.exceptions import HTTPError
 
-from benefits.core import models, session
+from benefits.core import session
 from benefits.core.middleware import (
     EligibleSessionRequired,
     VerifierSessionRequired,
@@ -26,7 +26,6 @@
 ROUTE_SUCCESS = "enrollment:success"
 ROUTE_TOKEN = "enrollment:token"
 
-TEMPLATE_INDEX = "enrollment/index.html"
 TEMPLATE_RETRY = "enrollment/retry.html"
 TEMPLATE_SUCCESS = "enrollment/success.html"
 
@@ -61,6 +60,7 @@ def index(request):
     session.update(request, origin=reverse(ROUTE_INDEX))
 
     agency = session.agency(request)
+    eligibility = session.eligibility(request)
     payment_processor = agency.payment_processor
 
     # POST back after payment processor form, process card token
@@ -69,9 +69,6 @@ def index(request):
         if not form.is_valid():
             raise Exception("Invalid card token form")
 
-        eligibility = session.eligibility(request)
-        logger.debug(f"Session contains an {models.EligibilityType.__name__}")
-
         logger.debug("Read tokenized card")
         card_token = form.cleaned_data.get("card_token")
 
@@ -122,7 +119,7 @@ def index(request):
 
         logger.debug(f'card_tokenize_url: {context["card_tokenize_url"]}')
 
-        return TemplateResponse(request, TEMPLATE_INDEX, context)
+        return TemplateResponse(request, eligibility.enrollment_index_template, context)
 
 
 @decorator_from_middleware(EligibleSessionRequired)


Test patch:
diff --git a/tests/pytest/core/test_models.py b/tests/pytest/core/test_models.py
--- a/tests/pytest/core/test_models.py
+++ b/tests/pytest/core/test_models.py
@@ -216,6 +216,16 @@ def test_EligibilityType_supports_expiration(model_EligibilityType_supports_expi
     model_EligibilityType_supports_expiration.full_clean()
 
 
+@pytest.mark.django_db
+def test_EligibilityType_enrollment_index_template(model_EligibilityType):
+    assert model_EligibilityType.enrollment_index_template == "enrollment/index.html"
+
+    model_EligibilityType.enrollment_index_template = "test/enrollment.html"
+    model_EligibilityType.save()
+
+    assert model_EligibilityType.enrollment_index_template == "test/enrollment.html"
+
+
 class SampleFormClass:
     """A class for testing EligibilityVerifier form references."""
 
diff --git a/tests/pytest/enrollment/test_views.py b/tests/pytest/enrollment/test_views.py
--- a/tests/pytest/enrollment/test_views.py
+++ b/tests/pytest/enrollment/test_views.py
@@ -13,7 +13,6 @@
     ROUTE_TOKEN,
     ROUTE_SUCCESS,
     ROUTE_RETRY,
-    TEMPLATE_INDEX,
     TEMPLATE_SUCCESS,
     TEMPLATE_RETRY,
 )
@@ -101,12 +100,12 @@ def test_token_valid(mocker, client):
 
 @pytest.mark.django_db
 @pytest.mark.usefixtures("mocked_session_agency", "mocked_session_verifier", "mocked_session_eligibility")
-def test_index_eligible_get(client):
+def test_index_eligible_get(client, model_EligibilityType):
     path = reverse(ROUTE_INDEX)
     response = client.get(path)
 
     assert response.status_code == 200
-    assert response.template_name == TEMPLATE_INDEX
+    assert response.template_name == model_EligibilityType.enrollment_index_template
     assert "forms" in response.context_data
     assert "cta_button" in response.context_data
     assert "card_tokenize_env" in response.context_data