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