Skip to content

Commit c093f7c

Browse files
authored
feat: add billing information support (#123)
1 parent 8bab572 commit c093f7c

File tree

7 files changed

+480
-48
lines changed

7 files changed

+480
-48
lines changed
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
<script lang="ts">
2+
import type { BuyerInfo, PaymentStep } from "../types";
3+
4+
export let currentPaymentStep: PaymentStep;
5+
6+
export let buyerInfo: BuyerInfo;
7+
8+
let errors: Partial<Record<keyof BuyerInfo, string>> = {};
9+
10+
function validateEmail(email: string): boolean {
11+
const re = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
12+
return re.test(email);
13+
}
14+
15+
function validateForm(): boolean {
16+
errors = {};
17+
let isValid = true;
18+
19+
if (!buyerInfo.email || !validateEmail(buyerInfo.email)) {
20+
errors.email = "Please enter a valid email address";
21+
isValid = false;
22+
}
23+
24+
if (!buyerInfo.firstName) {
25+
errors.firstName = "First name is required";
26+
isValid = false;
27+
}
28+
29+
if (!buyerInfo.lastName) {
30+
errors.lastName = "Last name is required";
31+
isValid = false;
32+
}
33+
34+
if (buyerInfo.phone && !/^\+?[1-9]\d{1,14}$/.test(buyerInfo.phone)) {
35+
errors.phone = "Please enter a valid phone number";
36+
isValid = false;
37+
}
38+
39+
return isValid;
40+
}
41+
42+
$: {
43+
buyerInfo = buyerInfo || {};
44+
buyerInfo.address = buyerInfo.address || {};
45+
}
46+
</script>
47+
48+
<div class="buyer-info-form">
49+
<h3>Buyer Information</h3>
50+
<form>
51+
<div class="form-group">
52+
<label for="email">Email</label>
53+
<input
54+
type="email"
55+
id="email"
56+
bind:value={buyerInfo.email}
57+
required
58+
placeholder="john.doe@example.com"
59+
/>
60+
{#if errors.email}<span class="error">{errors.email}</span>{/if}
61+
</div>
62+
<div class="form-row">
63+
<div class="form-group">
64+
<label for="firstName">First Name</label>
65+
<input
66+
type="text"
67+
id="firstName"
68+
bind:value={buyerInfo.firstName}
69+
required
70+
placeholder="John"
71+
/>
72+
{#if errors.firstName}<span class="error">{errors.firstName}</span>{/if}
73+
</div>
74+
<div class="form-group">
75+
<label for="lastName">Last Name</label>
76+
<input
77+
type="text"
78+
id="lastName"
79+
bind:value={buyerInfo.lastName}
80+
required
81+
placeholder="Doe"
82+
/>
83+
{#if errors.lastName}<span class="error">{errors.lastName}</span>{/if}
84+
</div>
85+
</div>
86+
<div class="form-row">
87+
<div class="form-group">
88+
<label for="businessName">Business Name</label>
89+
<input
90+
type="text"
91+
id="businessName"
92+
bind:value={buyerInfo.businessName}
93+
placeholder="Acme Inc."
94+
/>
95+
</div>
96+
<div class="form-group">
97+
<label for="phone">Phone</label>
98+
<input
99+
type="tel"
100+
id="phone"
101+
bind:value={buyerInfo.phone}
102+
placeholder="+1 (555) 123-4567"
103+
/>
104+
{#if errors.phone}<span class="error">{errors.phone}</span>{/if}
105+
</div>
106+
</div>
107+
<div class="form-group">
108+
<label for="street-address">Street Address</label>
109+
<input
110+
type="text"
111+
id="street-address"
112+
bind:value={buyerInfo.address["street-address"]}
113+
placeholder="123 Main St, Apt 4B"
114+
/>
115+
</div>
116+
<div class="form-row">
117+
<div class="form-group">
118+
<label for="locality">City</label>
119+
<input
120+
type="text"
121+
id="locality"
122+
bind:value={buyerInfo.address.locality}
123+
placeholder="San Francisco"
124+
/>
125+
</div>
126+
<div class="form-group">
127+
<label for="region">State/Province</label>
128+
<input
129+
type="text"
130+
id="region"
131+
bind:value={buyerInfo.address.region}
132+
placeholder="CA"
133+
/>
134+
</div>
135+
</div>
136+
<div class="form-row">
137+
<div class="form-group">
138+
<label for="country-name">Country</label>
139+
<input
140+
type="text"
141+
id="country-name"
142+
bind:value={buyerInfo.address["country-name"]}
143+
placeholder="United States"
144+
/>
145+
</div>
146+
<div class="form-group">
147+
<label for="postal-code">Postal Code</label>
148+
<input
149+
type="text"
150+
id="postal-code"
151+
bind:value={buyerInfo.address["postal-code"]}
152+
placeholder="94105"
153+
/>
154+
</div>
155+
</div>
156+
<div class="button-group">
157+
<button
158+
class="btn btn-secondary"
159+
type="button"
160+
on:click={() => {
161+
currentPaymentStep = "currency";
162+
}}>Back</button
163+
>
164+
<button
165+
type="button"
166+
on:click={() => {
167+
if (validateForm()) {
168+
currentPaymentStep = "confirmation";
169+
}
170+
}}
171+
class="btn">Continue to Payment</button
172+
>
173+
</div>
174+
</form>
175+
</div>
176+
177+
<style lang="scss">
178+
@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;500;600;700;800;900&display=swap");
179+
180+
.buyer-info-form {
181+
font-family: "Montserrat", sans-serif;
182+
color: #333;
183+
max-height: 100%;
184+
padding: 1rem;
185+
max-width: 90%;
186+
187+
h3 {
188+
margin: 0 0 1rem;
189+
font-size: 18px;
190+
font-weight: bold;
191+
}
192+
193+
form {
194+
display: flex;
195+
flex-direction: column;
196+
gap: 8px;
197+
}
198+
199+
.form-row {
200+
display: flex;
201+
gap: 8px;
202+
203+
input {
204+
width: 90% !important;
205+
}
206+
}
207+
208+
.form-group {
209+
display: flex;
210+
flex-direction: column;
211+
width: 100%;
212+
gap: 0.5rem;
213+
}
214+
215+
label {
216+
font-size: 14px;
217+
font-weight: 500;
218+
}
219+
220+
input {
221+
width: 100%;
222+
padding: 0.5rem;
223+
border: 1px solid #ccc;
224+
border-radius: 4px;
225+
font-size: 14px;
226+
font-family: inherit;
227+
228+
&:focus {
229+
outline: none;
230+
border-color: #0bb489;
231+
}
232+
}
233+
234+
.error {
235+
color: #ff4136;
236+
font-size: 12px;
237+
}
238+
239+
.button-group {
240+
display: flex;
241+
gap: 8px;
242+
align-items: center;
243+
margin-top: 1rem;
244+
}
245+
246+
.btn {
247+
display: inline-flex;
248+
cursor: pointer;
249+
color: white;
250+
align-items: center;
251+
justify-content: center;
252+
border-radius: 6px;
253+
font-size: 14px;
254+
font-weight: 500;
255+
background-color: #0bb489;
256+
border: none;
257+
outline: none;
258+
width: 100%;
259+
padding: 8px 16px;
260+
gap: 8px;
261+
262+
&:hover {
263+
background-color: darken(#0bb489, 10%);
264+
}
265+
266+
&:disabled {
267+
background-color: #cccccc;
268+
cursor: not-allowed;
269+
}
270+
}
271+
272+
.btn-secondary {
273+
background-color: #666;
274+
275+
&:hover {
276+
background-color: rgba($color: #666, $alpha: 0.8);
277+
}
278+
}
279+
}
280+
</style>

packages/payment-widget/src/lib/components/currency-selector.svelte

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@
66
77
export let currencies: Currency[];
88
export let selectedCurrency: Currency | null = null;
9-
export let currentPaymentStep: PaymentStep;
109
export let web3Modal: Web3Modal | null;
1110
export let isConnected: boolean;
11+
export let onCurrencySelected: () => void;
1212
1313
function selectCurrency(currency: Currency) {
1414
selectedCurrency = currency;
15-
currentPaymentStep = "confirmation";
1615
}
1716
</script>
1817

@@ -43,16 +42,20 @@
4342
</button>
4443
{/each}
4544
</div>
45+
<button
46+
class="btn"
47+
disabled={!selectedCurrency}
48+
on:click={onCurrencySelected}
49+
>
50+
Next
51+
</button>
4652
</div>
4753

4854
<style lang="scss">
4955
@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;500;600;700;800;900&display=swap");
5056
51-
body {
52-
font-family: "Montserrat", sans-serif;
53-
}
54-
5557
.currency-selector {
58+
font-family: "Montserrat", sans-serif;
5659
width: 100%;
5760
color: #333;
5861
@@ -65,6 +68,7 @@
6568
.currency-list {
6669
max-height: 300px;
6770
overflow-y: auto;
71+
margin-bottom: 20px;
6872
6973
&::-webkit-scrollbar {
7074
width: 6px;
@@ -137,4 +141,30 @@
137141
}
138142
}
139143
}
144+
145+
.btn {
146+
display: inline-flex;
147+
cursor: pointer;
148+
color: white;
149+
align-items: center;
150+
justify-content: center;
151+
border-radius: 6px;
152+
font-size: 14px;
153+
font-weight: 500;
154+
background-color: #0bb489;
155+
border: none;
156+
outline: none;
157+
width: 100%;
158+
padding: 8px 16px;
159+
gap: 8px;
160+
161+
&:hover:not(:disabled) {
162+
background-color: darken(#0bb489, 10%);
163+
}
164+
165+
&:disabled {
166+
background-color: #cccccc;
167+
cursor: not-allowed;
168+
}
169+
}
140170
</style>

0 commit comments

Comments
 (0)