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