Last active
August 3, 2025 15:59
-
-
Save thinkphp/cede6ac8164d7796cbf2920d3f63569b to your computer and use it in GitHub Desktop.
shipping-address.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!--- https://docs.stripe.com/checkout/embedded/quickstart?lang=php --> | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Shipping Address</title> | |
<style> | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
body { | |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
min-height: 100vh; | |
padding: 20px; | |
} | |
.container { | |
max-width: 800px; | |
margin: 0 auto; | |
background: white; | |
border-radius: 20px; | |
box-shadow: 0 20px 40px rgba(0,0,0,0.1); | |
overflow: hidden; | |
animation: slideUp 0.6s ease-out; | |
} | |
@keyframes slideUp { | |
from { | |
opacity: 0; | |
transform: translateY(30px); | |
} | |
to { | |
opacity: 1; | |
transform: translateY(0); | |
} | |
} | |
.header { | |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
color: white; | |
padding: 30px; | |
text-align: center; | |
} | |
.header h1 { | |
font-size: 2.5rem; | |
margin-bottom: 10px; | |
font-weight: 300; | |
} | |
.header p { | |
opacity: 0.9; | |
font-size: 1.1rem; | |
} | |
.form-container { | |
padding: 40px; | |
} | |
.form-row { | |
display: flex; | |
gap: 20px; | |
margin-bottom: 25px; | |
} | |
.form-group { | |
flex: 1; | |
position: relative; | |
} | |
.form-group.full-width { | |
flex: 100%; | |
} | |
label { | |
display: block; | |
margin-bottom: 8px; | |
font-weight: 600; | |
color: #333; | |
font-size: 0.95rem; | |
position: relative; | |
} | |
.required::after { | |
content: '*'; | |
color: #e74c3c; | |
margin-left: 4px; | |
} | |
input, select, textarea { | |
width: 100%; | |
padding: 15px; | |
border: 2px solid #e1e8ed; | |
border-radius: 12px; | |
font-size: 1rem; | |
transition: all 0.3s ease; | |
background: #fafbfc; | |
} | |
input:focus, select:focus, textarea:focus { | |
outline: none; | |
border-color: #667eea; | |
background: white; | |
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); | |
transform: translateY(-2px); | |
} | |
input.error, select.error { | |
border-color: #e74c3c; | |
background: #fdf2f2; | |
} | |
.error-message { | |
color: #e74c3c; | |
font-size: 0.85rem; | |
margin-top: 5px; | |
opacity: 0; | |
transform: translateY(-10px); | |
transition: all 0.3s ease; | |
} | |
.error-message.show { | |
opacity: 1; | |
transform: translateY(0); | |
} | |
.checkbox-group { | |
margin: 30px 0; | |
padding: 20px; | |
background: #f8f9fa; | |
border-radius: 12px; | |
border: 2px solid #e9ecef; | |
} | |
.checkbox-item { | |
display: flex; | |
align-items: center; | |
margin-bottom: 15px; | |
cursor: pointer; | |
} | |
.checkbox-item:last-child { | |
margin-bottom: 0; | |
} | |
.checkbox-item input[type="checkbox"] { | |
width: 20px; | |
height: 20px; | |
margin-right: 12px; | |
cursor: pointer; | |
accent-color: #667eea; | |
} | |
.checkbox-item label { | |
margin: 0; | |
cursor: pointer; | |
font-weight: normal; | |
} | |
.billing-address { | |
margin-top: 20px; | |
padding: 20px; | |
background: #f8f9fa; | |
border-radius: 12px; | |
border: 2px solid #e9ecef; | |
display: none; | |
animation: fadeIn 0.3s ease; | |
} | |
.billing-address.show { | |
display: block; | |
} | |
@keyframes fadeIn { | |
from { opacity: 0; transform: translateY(10px); } | |
to { opacity: 1; transform: translateY(0); } | |
} | |
.button-group { | |
display: flex; | |
gap: 15px; | |
margin-top: 40px; | |
} | |
.btn { | |
flex: 1; | |
padding: 18px; | |
border: none; | |
border-radius: 12px; | |
font-size: 1.1rem; | |
font-weight: 600; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
text-transform: uppercase; | |
letter-spacing: 0.5px; | |
} | |
.btn-primary { | |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
color: white; | |
} | |
.btn-primary:hover { | |
transform: translateY(-3px); | |
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3); | |
} | |
.btn-secondary { | |
background: #6c757d; | |
color: white; | |
} | |
.btn-secondary:hover { | |
background: #5a6268; | |
transform: translateY(-2px); | |
} | |
.progress-bar { | |
height: 4px; | |
background: #e9ecef; | |
margin-bottom: 30px; | |
} | |
.progress-fill { | |
height: 100%; | |
background: linear-gradient(90deg, #667eea, #764ba2); | |
width: 66%; | |
transition: width 0.3s ease; | |
} | |
@media (max-width: 768px) { | |
.form-row { | |
flex-direction: column; | |
gap: 15px; | |
} | |
.container { | |
margin: 10px; | |
border-radius: 15px; | |
} | |
.form-container { | |
padding: 25px; | |
} | |
.header { | |
padding: 25px; | |
} | |
.header h1 { | |
font-size: 2rem; | |
} | |
.button-group { | |
flex-direction: column; | |
} | |
} | |
.success-message { | |
background: #d4edda; | |
color: #155724; | |
padding: 15px; | |
border-radius: 8px; | |
margin-bottom: 20px; | |
border: 1px solid #c3e6cb; | |
display: none; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<div class="header"> | |
<h1>🎨 Shipping Address</h1> | |
<p>Complete your details for artwork delivery</p> | |
</div> | |
<div class="progress-bar"> | |
<div class="progress-fill"></div> | |
</div> | |
<div class="form-container"> | |
<div class="success-message" id="successMessage"> | |
✅ Your information has been saved successfully! | |
</div> | |
<form id="shippingForm" method="POST" action="process_shipping.php"> | |
<!-- Personal Information --> | |
<h3 style="margin-bottom: 25px; color: #333; font-size: 1.3rem;">📋 Personal Information</h3> | |
<div class="form-row"> | |
<div class="form-group"> | |
<label for="firstName" class="required">First Name</label> | |
<input type="text" id="firstName" name="firstName" required> | |
<div class="error-message" id="firstNameError"></div> | |
</div> | |
<div class="form-group"> | |
<label for="lastName" class="required">Last Name</label> | |
<input type="text" id="lastName" name="lastName" required> | |
<div class="error-message" id="lastNameError"></div> | |
</div> | |
</div> | |
<div class="form-row"> | |
<div class="form-group"> | |
<label for="email" class="required">Email Address</label> | |
<input type="email" id="email" name="email" required> | |
<div class="error-message" id="emailError"></div> | |
</div> | |
<div class="form-group"> | |
<label for="phone" class="required">Phone Number</label> | |
<input type="tel" id="phone" name="phone" required> | |
<div class="error-message" id="phoneError"></div> | |
</div> | |
</div> | |
<!-- Shipping Address --> | |
<h3 style="margin: 40px 0 25px 0; color: #333; font-size: 1.3rem;">🏠 Shipping Address</h3> | |
<div class="form-row"> | |
<div class="form-group"> | |
<label for="country" class="required">Country</label> | |
<select id="country" name="country" required> | |
<option value="">Select Country</option> | |
<optgroup label="Europe"> | |
<option value="RO">Romania</option> | |
<option value="DE">Germany</option> | |
<option value="FR">France</option> | |
<option value="IT">Italy</option> | |
<option value="ES">Spain</option> | |
<option value="GB">United Kingdom</option> | |
<option value="NL">Netherlands</option> | |
<option value="BE">Belgium</option> | |
<option value="AT">Austria</option> | |
<option value="CH">Switzerland</option> | |
<option value="SE">Sweden</option> | |
<option value="NO">Norway</option> | |
<option value="DK">Denmark</option> | |
<option value="FI">Finland</option> | |
<option value="PL">Poland</option> | |
<option value="CZ">Czech Republic</option> | |
<option value="HU">Hungary</option> | |
<option value="BG">Bulgaria</option> | |
<option value="GR">Greece</option> | |
<option value="PT">Portugal</option> | |
<option value="IE">Ireland</option> | |
<option value="HR">Croatia</option> | |
<option value="SI">Slovenia</option> | |
<option value="SK">Slovakia</option> | |
<option value="EE">Estonia</option> | |
<option value="LV">Latvia</option> | |
<option value="LT">Lithuania</option> | |
<option value="LU">Luxembourg</option> | |
<option value="MT">Malta</option> | |
<option value="CY">Cyprus</option> | |
</optgroup> | |
<optgroup label="North America"> | |
<option value="US">United States</option> | |
<option value="CA">Canada</option> | |
<option value="MX">Mexico</option> | |
</optgroup> | |
<optgroup label="Asia"> | |
<option value="JP">Japan</option> | |
<option value="CN">China</option> | |
<option value="KR">South Korea</option> | |
<option value="SG">Singapore</option> | |
<option value="HK">Hong Kong</option> | |
<option value="TW">Taiwan</option> | |
<option value="IN">India</option> | |
<option value="TH">Thailand</option> | |
<option value="MY">Malaysia</option> | |
<option value="PH">Philippines</option> | |
<option value="ID">Indonesia</option> | |
<option value="VN">Vietnam</option> | |
</optgroup> | |
<optgroup label="Oceania"> | |
<option value="AU">Australia</option> | |
<option value="NZ">New Zealand</option> | |
</optgroup> | |
<optgroup label="South America"> | |
<option value="BR">Brazil</option> | |
<option value="AR">Argentina</option> | |
<option value="CL">Chile</option> | |
<option value="CO">Colombia</option> | |
<option value="PE">Peru</option> | |
<option value="UY">Uruguay</option> | |
</optgroup> | |
<optgroup label="Africa"> | |
<option value="ZA">South Africa</option> | |
<option value="EG">Egypt</option> | |
<option value="MA">Morocco</option> | |
<option value="NG">Nigeria</option> | |
<option value="KE">Kenya</option> | |
</optgroup> | |
<optgroup label="Middle East"> | |
<option value="AE">United Arab Emirates</option> | |
<option value="SA">Saudi Arabia</option> | |
<option value="IL">Israel</option> | |
<option value="TR">Turkey</option> | |
<option value="QA">Qatar</option> | |
<option value="KW">Kuwait</option> | |
</optgroup> | |
</select> | |
<div class="error-message" id="countryError"></div> | |
</div> | |
<div class="form-group"> | |
<label for="state" class="required">State/Province/Region</label> | |
<input type="text" id="state" name="state" required placeholder="e.g. California, Bucharest, Bayern"> | |
<div class="error-message" id="stateError"></div> | |
</div> | |
</div> | |
<div class="form-row"> | |
<div class="form-group"> | |
<label for="city" class="required">City</label> | |
<input type="text" id="city" name="city" required> | |
<div class="error-message" id="cityError"></div> | |
</div> | |
<div class="form-group"> | |
<label for="postalCode" class="required">Postal Code/ZIP</label> | |
<input type="text" id="postalCode" name="postalCode" required placeholder="e.g. 010101, 90210, SW1A 1AA"> | |
<div class="error-message" id="postalCodeError"></div> | |
</div> | |
</div> | |
<div class="form-group full-width"> | |
<label for="address" class="required">Street Address</label> | |
<input type="text" id="address" name="address" required placeholder="e.g. 123 Main Street, Apt 4B"> | |
<div class="error-message" id="addressError"></div> | |
</div> | |
<div class="form-group full-width"> | |
<label for="notes">Special Instructions for Artwork Delivery</label> | |
<textarea id="notes" name="notes" rows="3" placeholder="e.g. Weekdays only, fragile handling required, doorman available..."></textarea> | |
</div> | |
<div class="button-group"> | |
<button type="button" class="btn btn-secondary" onclick="goBack()"> | |
← Back to Cart | |
</button> | |
<button type="submit" class="btn btn-primary"> | |
Continue to Payment → | |
</button> | |
</div> | |
</form> | |
</div> | |
</div> | |
<script> | |
// Form validation | |
const form = document.getElementById('shippingForm'); | |
const fields = ['firstName', 'lastName', 'email', 'phone', 'country', 'state', 'city', 'postalCode', 'address']; | |
// Validators | |
const validators = { | |
firstName: (value) => value.length >= 2 ? null : 'First name must be at least 2 characters', | |
lastName: (value) => value.length >= 2 ? null : 'Last name must be at least 2 characters', | |
email: (value) => { | |
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | |
return emailRegex.test(value) ? null : 'Please enter a valid email address'; | |
}, | |
phone: (value) => { | |
const phoneRegex = /^[0-9+\-\s\(\)]{8,}$/; | |
return phoneRegex.test(value) ? null : 'Please enter a valid phone number'; | |
}, | |
country: (value) => value ? null : 'Please select a country', | |
state: (value) => value.length >= 2 ? null : 'Please enter state/province/region', | |
city: (value) => value.length >= 2 ? null : 'City must be at least 2 characters', | |
postalCode: (value) => { | |
// Flexible validation for international postal codes | |
return value.length >= 3 ? null : 'Please enter a valid postal code'; | |
}, | |
address: (value) => value.length >= 5 ? null : 'Address must be at least 5 characters' | |
}; | |
// Add event listeners for real-time validation | |
fields.forEach(fieldName => { | |
const field = document.getElementById(fieldName); | |
const errorElement = document.getElementById(fieldName + 'Error'); | |
field.addEventListener('blur', () => validateField(fieldName)); | |
field.addEventListener('input', () => { | |
if (field.classList.contains('error')) { | |
validateField(fieldName); | |
} | |
}); | |
}); | |
function validateField(fieldName) { | |
const field = document.getElementById(fieldName); | |
const errorElement = document.getElementById(fieldName + 'Error'); | |
const error = validators[fieldName] ? validators[fieldName](field.value.trim()) : null; | |
if (error) { | |
field.classList.add('error'); | |
errorElement.textContent = error; | |
errorElement.classList.add('show'); | |
return false; | |
} else { | |
field.classList.remove('error'); | |
errorElement.classList.remove('show'); | |
return true; | |
} | |
} | |
// Toggle billing address | |
// Removed - no longer needed | |
// Form submission handling | |
form.addEventListener('submit', function(e) { | |
e.preventDefault(); | |
let isValid = true; | |
fields.forEach(fieldName => { | |
if (!validateField(fieldName)) { | |
isValid = false; | |
} | |
}); | |
if (isValid) { | |
// Simulate saving data | |
const successMessage = document.getElementById('successMessage'); | |
successMessage.style.display = 'block'; | |
// Scroll to top | |
window.scrollTo({top: 0, behavior: 'smooth'}); | |
// After 2 seconds, redirect to payment page | |
setTimeout(() => { | |
// Here you could redirect to actual payment processing | |
console.log('Redirecting to payment processing...'); | |
// window.location.href = 'payment.php'; | |
// For demo, just hide the message | |
successMessage.style.display = 'none'; | |
}, 2000); | |
// Here you could send data via AJAX | |
const formData = new FormData(form); | |
console.log('Form data:', Object.fromEntries(formData)); | |
} else { | |
// Scroll to first error field | |
const firstError = document.querySelector('.error'); | |
if (firstError) { | |
firstError.scrollIntoView({behavior: 'smooth', block: 'center'}); | |
} | |
} | |
}); | |
function goBack() { | |
// Redirect back to shopping cart | |
window.history.back(); | |
// or window.location.href = 'cart.php'; | |
} | |
// Auto-complete for cities based on country | |
document.getElementById('country').addEventListener('change', function() { | |
const stateField = document.getElementById('state'); | |
const cityField = document.getElementById('city'); | |
const selectedCountry = this.value; | |
// Update placeholder for state/province based on country | |
if (selectedCountry === 'US') { | |
stateField.placeholder = 'e.g. California, New York, Texas'; | |
} else if (selectedCountry === 'RO') { | |
stateField.placeholder = 'e.g. Bucharest, Cluj, Timis'; | |
} else if (selectedCountry === 'DE') { | |
stateField.placeholder = 'e.g. Bayern, Berlin, Hamburg'; | |
} else if (['GB', 'FR', 'IT', 'ES'].includes(selectedCountry)) { | |
stateField.placeholder = 'e.g. London, Paris, Rome, Madrid'; | |
} else { | |
stateField.placeholder = 'State/Province/Region'; | |
} | |
stateField.focus(); | |
}); | |
// Automatic postal code formatting - flexible for all countries | |
document.getElementById('postalCode').addEventListener('input', function() { | |
const country = document.getElementById('country').value; | |
let value = this.value.toUpperCase(); | |
// Keep only alphanumeric characters and spaces for international postal codes | |
if (country === 'RO') { | |
// Romania - digits only, 6 characters | |
value = value.replace(/\D/g, '').substring(0, 6); | |
} else if (country === 'US') { | |
// USA - format XXXXX or XXXXX-XXXX | |
value = value.replace(/[^0-9]/g, ''); | |
if (value.length > 5) { | |
value = value.substring(0, 5) + '-' + value.substring(5, 9); | |
} | |
} else if (country === 'GB') { | |
// UK - complex format, keep all characters | |
value = value.replace(/[^A-Z0-9\s]/g, '').substring(0, 8); | |
} else if (country === 'CA') { | |
// Canada - format AXA XAX | |
value = value.replace(/[^A-Z0-9]/g, ''); | |
if (value.length > 3) { | |
value = value.substring(0, 3) + ' ' + value.substring(3, 6); | |
} | |
} else { | |
// Other countries - general format | |
value = value.replace(/[^A-Z0-9\s-]/g, '').substring(0, 10); | |
} | |
this.value = value; | |
}); | |
// Automatic phone formatting | |
document.getElementById('phone').addEventListener('input', function() { | |
let value = this.value.replace(/\D/g, ''); | |
// Keep international format flexible | |
this.value = value; | |
}); | |
// Additional animations | |
window.addEventListener('load', function() { | |
const formGroups = document.querySelectorAll('.form-group'); | |
formGroups.forEach((group, index) => { | |
setTimeout(() => { | |
group.style.opacity = '0'; | |
group.style.transform = 'translateY(20px)'; | |
group.style.transition = 'all 0.4s ease'; | |
setTimeout(() => { | |
group.style.opacity = '1'; | |
group.style.transform = 'translateY(0)'; | |
}, 50); | |
}, index * 50); | |
}); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment