Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplified Employer Search #17

Merged
merged 4 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/assets/images/uswds.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
//= link @uswds/uswds/dist/img/icon-dot-gov.svg
//= link @uswds/uswds/dist/img/icon-https.svg
//= link @uswds/uswds/dist/img/usa-icons/close.svg
//= link @uswds/uswds/dist/img/usa-icons-bg/search--white.svg
2 changes: 0 additions & 2 deletions app/controllers/api/argyle_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
class Api::ArgyleController < ApplicationController
skip_before_action :verify_authenticity_token
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒


USER_TOKEN_ENDPOINT = 'https://api-sandbox.argyle.com/v2/user-tokens';

def update_token
Expand Down
11 changes: 8 additions & 3 deletions app/controllers/cbv_flows_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ def entry

def employer_search
@argyle_user_token = fetch_and_store_argyle_token
@companies = fetch_employers
@query = search_params[:query]
@employers = @query.blank? ? [] : fetch_employers(@query)
end

def summary
Expand Down Expand Up @@ -71,8 +72,8 @@ def fetch_and_store_argyle_token
parsed['user_token']
end

def fetch_employers
res = Net::HTTP.get(URI.parse(ITEMS_ENDPOINT), {"Authorization" => "Basic #{Rails.application.credentials.argyle[:api_key]}"})
def fetch_employers(query = '')
res = Net::HTTP.get(URI.parse("#{ITEMS_ENDPOINT}?mapping_status=verified,mapped&q=#{query}"), {"Authorization" => "Basic #{Rails.application.credentials.argyle[:api_key]}"})
parsed = JSON.parse(res)

parsed['results']
Expand All @@ -84,4 +85,8 @@ def fetch_payroll

parsed['results']
end

def search_params
params.permit(:query)
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You didn't actually have to do this since the params aren't being passed into an ActiveRecord model, and you're extracting exactly the param key you want, but I guess it never hurts!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tdooner oooh! I never know when to use #require. Usually a Rails form wraps the params in a key named after the model or something, but that's now how this is used. Happy to remove.

end
8 changes: 7 additions & 1 deletion app/javascript/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ import "@uswds/uswds"
// make sure USWDS components are wired to their behavior after a Turbo navigation
import components from "@uswds/uswds/src/js/components"
let initialLoad = true;

document.addEventListener("turbo:load", () => {
if (initialLoad) {
// initial domready is handled by `import "uswds"` code
initialLoad = false
return
}

const target = document.body
Object.keys(components).forEach((key) => {
const behavior = components[key]
behavior.on(target)
})
})
});

document.addEventListener("turbo:frame-render", () => {
initialLoad = true;
});
35 changes: 9 additions & 26 deletions app/javascript/controllers/cbv_flows_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,20 @@ import * as ActionCable from '@rails/actioncable'
import metaContent from "../utilities/meta";
import { loadArgyle, initializeArgyle, updateToken } from "../utilities/argyle"

function toOptionHTML({ value }) {
return `<option value='${value}'>${value}</option>`;
}

export default class extends Controller {
static targets = ["options", "continue", "userAccountId", "fullySynced", "form", "modal"];

selection = null;
static targets = [
"form",
"searchTerms",
"userAccountId",
"modal"
];

argyle = null;

argyleUserToken = null;

cable = ActionCable.createConsumer();

// TODO: information stored on the CbvFlow model can infer whether the paystubs are sync'd
// by checking the value of payroll_data_available_from. We should make that the initial value.
fullySynced = false;

connect() {
// check for this value when connected
this.argyleUserToken = metaContent('argyle_user_token');
Expand All @@ -34,10 +29,7 @@ export default class extends Controller {
console.log("Disconnected");
},
received: (data) => {
console.log("Received some data:", data);
if (data.event === 'paystubs.fully_synced' || data.event === 'paystubs.partially_synced') {
this.fullySynced = true;

this.formTarget.submit();
}
}
Expand All @@ -54,23 +46,14 @@ export default class extends Controller {
console.log(event);
}

search(event) {
const input = event.target.value;
this.optionsTarget.innerHTML = [this.optionsTarget.innerHTML, toOptionHTML({ value: input })].join('');
}

select(event) {
this.selection = event.detail;

this.continueTarget.disabled = false;
this.submit(event.target.dataset.itemId);
}

submit(event) {
event.preventDefault();

submit(itemId) {
loadArgyle()
.then(Argyle => initializeArgyle(Argyle, this.argyleUserToken, {
items: [this.selection.value],
items: [itemId],
onAccountConnected: this.onSignInSuccess.bind(this),
onAccountError: this.onAccountError.bind(this),
// Unsure what these are for!
Expand Down
8 changes: 7 additions & 1 deletion app/javascript/utilities/argyle.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import loadScript from 'load-script';
import metaContent from "./meta";
import CSRF from './csrf';

const ARGYLE_TOKENS_REFRESH = '/api/argyle/tokens';

Expand All @@ -24,7 +25,12 @@ export function initializeArgyle(Argyle, userToken, callbacks) {
}

export const updateToken = async updateToken => {
const response = await fetch(ARGYLE_TOKENS_REFRESH, { method: 'post' }).then(response => response.json());
const response = await fetch(ARGYLE_TOKENS_REFRESH, {
method: 'post',
headers: {
'X-CSRF-Token': CSRF.token,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Rails used to have a way to do this out-of-the-box, but I googled and it looks like it was deprecated in Rails 7. If we end up making a lot of these fetch requests, maybe there is some way we can abstract this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Login.gov (lol) has a pretty good library for this but it's not published. Could "crib" it instead

},
}).then(response => response.json());

updateToken(response.token);
}
36 changes: 36 additions & 0 deletions app/javascript/utilities/csrf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Borrowed from https://github.com/18F/identity-idp/blob/59bc8bb6c47402f386d9248bfad3c0803f68187e/app/javascript/packages/request/index.ts#L25-L59
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"borrowed", lol, make sure to give it back when you're done!

export default class CSRF {
static get token(): string | null {
return this.#tokenMetaElement?.content || null;
}

static set token(value: string | null) {
if (!value) {
return;
}

if (this.#tokenMetaElement) {
this.#tokenMetaElement.content = value;
}

this.#paramInputElements.forEach((input) => {
input.value = value;
});
}

static get param(): string | undefined {
return this.#paramMetaElement?.content;
}

static get #tokenMetaElement(): HTMLMetaElement | null {
return document.querySelector('meta[name="csrf-token"]');
}

static get #paramMetaElement(): HTMLMetaElement | null {
return document.querySelector('meta[name="csrf-param"]');
}

static get #paramInputElements(): NodeListOf<HTMLInputElement> {
return document.querySelectorAll(`input[name="${this.param}"]`);
}
}
31 changes: 31 additions & 0 deletions app/views/cbv_flows/_employer.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<%= turbo_frame_tag 'employers' do %>
<div class="usa-card-group">
<% @employers.each do |employer| %>
<div class="usa-card usa-card--flag usa-card--media-right flex-1">
<div class="usa-card__container">
<div class="usa-card__header">
<h2 class="usa-card__heading"><%= employer['name'] %></h2>
</div>
<div class="display-none usa-card__media usa-card__media--inset">
<div class="usa-card__img">
<img
src="<%= employer['logo_url'] %>"
alt="A placeholder image"
/>
</div>
</div>
<div class="usa-card__footer">
<button
data-action="click->cbv-flows#select"
data-item-id="<%= employer['id'] %>"
class="usa-button usa-button--outline"
type="button"
>
Select
</button>
</div>
</div>
</div>
<% end %>
</div>
<% end %>
56 changes: 28 additions & 28 deletions app/views/cbv_flows/employer_search.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,39 @@
<h2>Get payment info from your employer.</h2>

<div data-controller="cbv-flows">
<%= form_with url: next_path, method: :post, data: { 'cbv-flows-target': "form" } do |f| %>
<label class="usa-label" for="employer">Search for an employer</label>
<div class="usa-combo-box margin-bottom-3" data-action="input->cbv-flows#search">
<select class="usa-select" name="employer" id="employer" data-cbv-flows-target="options" data-action="change->cbv-flows#select">
<option value>Select an employer</option>
<% @companies.each do |company| %>
<option value='<%= company['id'] %>'>
<%= company['name'] %>
</option>
<% end %>
</select>
<input type="hidden" name="user[account_id]" data-cbv-flows-target="userAccountId" />
</div>
<a
href="#example-modal-3"
class="display-none"
aria-controls="example-modal-3"
data-open-modal
data-cbv-flows-target="modal"
></a>
<%= f.submit "Continue",
class: "usa-button usa-button--outline",
disabled: "disabled",
type: "submit",
data: { action: "click->cbv-flows#submit", 'cbv-flows-target': "continue" }
%>
<button class="usa-button usa-button--outline" disabled="true" type="button">Add Another Employer</button>
<h3 class="site-preview-heading">Search for an employer</h3>
<%= form_with url: cbv_flow_employer_search_path, method: :get, class: 'usa-search usa-search--big', html: { role: 'search' }, data: { turbo_frame: 'employers', turbo_action: 'advance' } do |f| %>
<%= f.label :query, "Search for your employer", class: "usa-sr-only" %>
<%= f.text_field :query, value: @query, class: "usa-input", type: 'search', data: { "cbv-flows-target": "searchTerms" } %>
<button
class="usa-button"
type="submit"
>
<span class="usa-search__submit-text">
Search
</span>
<%= image_tag "@uswds/uswds/dist/img/usa-icons-bg/search--white.svg", class: "usa-search__submit-icon", alt: "Search" %>
</button>
<% end %>

<h3 class="site-preview-heading margin-bottom-2">Results</h3>
<%= render partial: 'employer', locals: { employer: @employers } %>

<%= form_with url: next_path, method: :post, class: 'display-none', data: { 'cbv-flows-target': "form" } do |f| %>
<input type="hidden" name="user[account_id]" data-cbv-flows-target="userAccountId" />
<% end %>

<a
href="#data-fetching-modal"
class="display-none"
aria-controls="data-fetching-modal"
data-open-modal
data-cbv-flows-target="modal"
></a>
<div class="margin-y-3">
<div
class="usa-modal"
id="example-modal-3"
id="data-fetching-modal"
aria-labelledby="modal-3-heading"
aria-describedby="modal-3-description"
data-force-action
Expand Down
2 changes: 1 addition & 1 deletion app/views/pages/home.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<h1>Welcome to Verify.gov</h1>
<h2>This website can help you get approved for Medicaid</h2>
<h2>This website can help you get approved for your benefits.</h2>

<p>Get started here by requesting a link from your caseworker.</p>

Expand Down
5 changes: 3 additions & 2 deletions db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified doc/compliance/rendered/apps/data.logical.pdf
Binary file not shown.
Loading