esim_adapter.slim

  1- content_for :head do
  2	== assets [:css, :loader]
  3	== assets [:css, :tom_select]
  4	== assets [:js, :tom_select]
  5	== assets [:js, :htmx]
  6	script src="https://js.braintreegateway.com/web/dropin/1.42.0/js/dropin.js"
  7	javascript:
  8		window.addEventListener("load", function() {
  9			const country = new TomSelect("select[name=country-name]", {
 10				plugins: [
 11					"change_listener",
 12					"no_backspace_delete",
 13					"dropdown_input"
 14				],
 15				highlight: false,
 16				maxOptions: 500,
 17				onChange: (value) => {
 18					document.querySelector("input[name=postal-code]").previousSibling.textContent = value === "US" ? "Zip Code" : "Postal Code";
 19					document.querySelector("select[name=currency]").value = value === "CA" ? "CAD" : "USD";
 20					document.querySelector("select[name=currency]").dispatchEvent(new Event("change"));
 21				}
 22			});
 23
 24			document.querySelector("select[name=currency]").addEventListener("change", (e) => {
 25				if (e.target.value === "CAD") {
 26					document.querySelector("input[name=esim_adapter_quantity]").previousSibling.textContent = "eSIM Adapters ($54.99 each)"
 27					document.querySelector("input[name=pcsc_quantity]").previousSibling.textContent = "USB PCSC Readers ($13.50 each)"
 28				} else {
 29					document.querySelector("input[name=esim_adapter_quantity]").previousSibling.textContent = "eSIM Adapters ($39.99 each)"
 30					document.querySelector("input[name=pcsc_quantity]").previousSibling.textContent = "USB PCSC Readers ($10.00 each)"
 31				}
 32			});
 33
 34			document.querySelector("input[name=pcsc_quantity]").addEventListener("change", (e) => {
 35				document.querySelector("#compatible").style.display = e.target.value < 1 ? "flex" : "none";
 36				document.querySelector("#compatible input").required = e.target.value < 1;
 37			});
 38
 39			country.trigger("change", document.querySelector("select[name=country-name]").value);
 40		});
 41
 42scss:
 43	body {
 44		font-family: sans-serif;
 45	}
 46	h1 {
 47		text-align: center;
 48	}
 49	form {
 50		display: block;
 51		max-width: 40em;
 52		margin: auto;
 53		* { box-sizing: border-box; }
 54		label {
 55			display: block;
 56			margin-top: 1em;
 57		}
 58		.half {
 59			margin-top: 1em;
 60			display: flex;
 61			& > * {
 62				flex: 1;
 63			}
 64			& > span {
 65				padding: 8px 0;
 66			}
 67		}
 68		button {
 69			display: block;
 70			margin: 1em auto;
 71			padding: 0.5em;
 72			font-size: 1.2em;
 73		}
 74		#compatible input {
 75			flex: 0 0 2em;
 76		}
 77	}
 78	.htmx-indicator {
 79		margin-top: 1em;
 80		text-align: center;
 81		float: right;
 82	}
 83
 84section data-hx-get="/esim-adapter/total" data-hx-trigger="input delay:1s,change" data-hx-include="form" data-hx-target="#total"
 85	h1 Order an eSIM Adapter
 86
 87	form method="post" action=""
 88		label
 89			div Country for Shipping Address
 90			select name="country-name" required=true
 91				- ISO3166::Country.all.each do |country|
 92					option selected=(country.alpha2 == "US") value=country.alpha2 = "#{country.emoji_flag} #{country.translation('en')}"
 93
 94		label
 95			div Currency
 96			select.ts-control name="currency" data-hx-get="/esim-adapter/braintree" data-hx-trigger="change" data-hx-target="#braintree-js"
 97				option value="USD" US Dollars
 98				option value="CAD" Canadian Dollars
 99
100		label.half
101			span eSIM Adapters ($39.99 each)
102			input.ts-control name="esim_adapter_quantity" type="number" step="1" min="0" required=true value=1
103
104		label.half
105			span USB PCSC Readers ($10 each)
106			input.ts-control name="pcsc_quantity" type="number" step="1" min="0" required=true value=0
107
108		label
109			div Zip Code
110			input.ts-control type="text" name="postal-code" required=true
111
112		label
113			div Street Address
114			input.ts-control type="text" name="street-address" required=true
115
116		label
117			div Email Address
118			input.ts-control type="email" name="email" required=true
119
120		label.half
121			div Cardholder Given Name
122			input.ts-control type="text" name="given-name" required=true
123
124		label.half
125			div Cardholder Surname
126			input.ts-control type="text" name="family-name" required=true
127
128		.htmx-indicator
129			.lds-ring <div></div><div></div><div></div><div></div></div>
130		#total
131
132		#braintree
133		input type="hidden" name="atfd" value=antifraud
134		input type="hidden" name="braintree_nonce"
135
136		small
137			| Charges will come from MBOA or Soprani.ca Telecom
138
139		label.half#compatible
140			span I have verified that my Android device is compatible with <a target="_blank" href="https://jmp.chat/esim-adapter">the app</a>
141			input type="checkbox" required=true
142
143		button Place Order
144
145javascript:
146	if(window.localStorage) {
147		var atfd = localStorage.getItem("atfd");
148		if(!atfd) {
149			atfd = #{{antifraud.to_json}};
150			localStorage.setItem("atfd", atfd);
151		}
152		document.querySelector("input[name=atfd]").value = atfd;
153	}
154
155	window.braintreeInstance = null;
156	document.querySelector("form").addEventListener("submit", function(e) {
157		e.preventDefault();
158		if (!braintreeInstance || !document.querySelector("input[name=amount]")) {
159			return;
160		}
161		document.querySelector("form button").disabled = true;
162		document.querySelector("form button").style.display = "none";
163
164		braintreeInstance.requestPaymentMethod({
165			threeDSecure: {
166				amount: document.querySelector("*[name=amount]").value,
167				requireChallenge: true,
168				challengeRequested: true,
169				collectDeviceData: true,
170				email: document.querySelector("*[name=email]").value,
171				billingAddress: {
172					givenName: document.querySelector("*[name=given-name]").value,
173					surname: document.querySelector("*[name=family-name]").value,
174					postalCode: document.querySelector("*[name=postal-code]").value,
175					streetAddress: document.querySelector("*[name=street-address]").value,
176					countryCodeAlpha2: document.querySelector("*[name=country-name]").value
177				},
178				additionalInformation: {
179					ipAddress: #{{ip.to_json}}
180				}
181			}
182		}, function(err, payload) {
183			if(err) {
184				console.log(err);
185				document.querySelector("form button").disabled = false;
186				document.querySelector("form button").style.display = "block";
187			} else {
188				e.target.braintree_nonce.value = payload.nonce;
189				e.target.submit();
190			}
191		});
192	});
193
194#braintree-js