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 .htmx-indicator
121 .lds-ring <div></div><div></div><div></div><div></div></div>
122 #total
123
124 #braintree
125 input type="hidden" name="atfd" value=antifraud
126 input type="hidden" name="braintree_nonce"
127
128 small
129 | Charges will come from MBOA or Soprani.ca Telecom
130
131 label.half#compatible
132 span I have verified that my Android device is compatible with <a target="_blank" href="https://jmp.chat/esim-adapter">the app</a>
133 input type="checkbox" required=true
134
135 button Place Order
136
137javascript:
138 if(window.localStorage) {
139 var atfd = localStorage.getItem("atfd");
140 if(!atfd) {
141 atfd = "#{antifraud}";
142 localStorage.setItem("atfd", atfd);
143 }
144 document.querySelector("input[name=atfd]").value = atfd;
145 }
146
147 window.braintreeInstance = null;
148 document.querySelector("form").addEventListener("submit", function(e) {
149 e.preventDefault();
150 if (!braintreeInstance || !document.querySelector("input[name=amount]")) {
151 return;
152 }
153 document.querySelector("form button").disabled = true;
154 document.querySelector("form button").style.display = "none";
155
156 braintreeInstance.requestPaymentMethod({
157 threeDSecure: {
158 amount: document.querySelector("input[name=amount]").value,
159 requireChallenge: true,
160 challengeRequested: true,
161 collectDeviceData: true,
162 email: document.querySelector("input[name=email]").value
163 }
164 }, function(err, payload) {
165 if(err) {
166 console.log(err);
167 document.querySelector("form button").disabled = false;
168 document.querySelector("form button").style.display = "block";
169 } else {
170 e.target.braintree_nonce.value = payload.nonce;
171 e.target.submit();
172 }
173 });
174 });
175
176#braintree-js