add favorite

This commit is contained in:
Phuoc Nguyen
2025-11-18 11:23:07 +07:00
parent 192c322816
commit a5eb95fa64
25 changed files with 2506 additions and 978 deletions

521
html/address-create.html Normal file
View File

@@ -0,0 +1,521 @@
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Thêm địa chỉ mới - EuroTile Worker</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="assets/css/style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body class="bg-gray-50">
<div class="page-wrapper">
<!-- Header -->
<div class="header">
<a href="addresses.html" class="back-button">
<i class="fas fa-arrow-left"></i>
</a>
<h1 class="header-title">Thêm địa chỉ mới</h1>
<button class="back-button" onclick="openInfoModal()">
<i class="fas fa-info-circle"></i>
</button>
</div>
<div class="container max-w-3xl mx-auto px-4 py-6" style="padding-bottom: 100px;">
<form id="addressForm" onsubmit="handleSubmit(event)">
<!-- Contact Information -->
<div class="bg-white rounded-lg shadow-sm p-4 mb-4">
<h3 class="text-base font-semibold text-gray-900 mb-4 flex items-center gap-2">
<i class="fas fa-user text-blue-600"></i>
Thông tin liên hệ
</h3>
<div class="form-group mb-4">
<label class="form-label block text-sm font-medium text-gray-700 mb-2">
Họ và tên <span class="text-red-500">*</span>
</label>
<div class="relative">
<i class="fas fa-user absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
<input type="text"
id="fullName"
class="form-input w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
placeholder="Nhập họ và tên người nhận"
required>
</div>
</div>
<div class="form-group mb-4">
<label class="form-label block text-sm font-medium text-gray-700 mb-2">
Số điện thoại <span class="text-red-500">*</span>
</label>
<div class="relative">
<i class="fas fa-phone absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
<input type="tel"
id="phone"
class="form-input w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
placeholder="Nhập số điện thoại"
pattern="[0-9]{10,11}"
required>
</div>
<p class="text-xs text-gray-500 mt-1">Định dạng: 10-11 số</p>
</div>
<div class="form-group mb-4">
<label class="form-label block text-sm font-medium text-gray-700 mb-2">
Email <span class="text-red-500"></span>
</label>
<div class="relative">
<i class="fas fa-phone absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
<input type="tel"
id="phone"
class="form-input w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
placeholder="Nhập email"
pattern="[0-9]{10,11}"
required>
</div>
</div>
<div class="form-group mb-4">
<label class="form-label block text-sm font-medium text-gray-700 mb-2">
Mã số thuế <span class="text-red-500"></span>
</label>
<div class="relative">
<i class="fas fa-phone absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
<input type="tel"
id="phone"
class="form-input w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
placeholder="Nhập mã số thuế"
pattern="[0-9]{10,11}"
required>
</div>
</div>
</div>
<!-- Address Information -->
<div class="bg-white rounded-lg shadow-sm p-4 mb-4">
<h3 class="text-base font-semibold text-gray-900 mb-4 flex items-center gap-2">
<i class="fas fa-map-marker-alt text-blue-600"></i>
Địa chỉ giao hàng
</h3>
<div class="form-group mb-4">
<label class="form-label block text-sm font-medium text-gray-700 mb-2">
Tỉnh/Thành phố <span class="text-red-500">*</span>
</label>
<div class="relative">
<select id="province"
class="form-select w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition appearance-none bg-white"
onchange="updateDistricts()"
required>
<option value="">-- Chọn Tỉnh/Thành phố --</option>
<option value="hanoi">Hà Nội</option>
<option value="hcm">TP. Hồ Chí Minh</option>
<option value="danang">Đà Nẵng</option>
<option value="haiphong">Hải Phòng</option>
<option value="cantho">Cần Thơ</option>
<option value="binhduong">Bình Dương</option>
<option value="dongnai">Đồng Nai</option>
<option value="vungtau">Bà Rịa - Vũng Tàu</option>
<option value="nhatrang">Khánh Hòa</option>
</select>
<i class="fas fa-chevron-down absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 pointer-events-none"></i>
</div>
</div>
<div class="form-group mb-4">
<label class="form-label block text-sm font-medium text-gray-700 mb-2">
Phường/Xã <span class="text-red-500">*</span>
</label>
<div class="relative">
<select id="district"
class="form-select w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition appearance-none bg-white"
onchange="updateWards()"
required
disabled>
<option value="">-- Chọn Phường/Xã --</option>
</select>
<i class="fas fa-chevron-down absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 pointer-events-none"></i>
</div>
</div>
<!--<div class="form-group mb-4">
<label class="form-label block text-sm font-medium text-gray-700 mb-2">
Phường/Xã <span class="text-red-500">*</span>
</label>
<div class="relative">
<select id="ward"
class="form-select w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition appearance-none bg-white"
required
disabled>
<option value="">-- Chọn Phường/Xã --</option>
</select>
<i class="fas fa-chevron-down absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 pointer-events-none"></i>
</div>
</div>-->
<div class="form-group mb-4">
<label class="form-label block text-sm font-medium text-gray-700 mb-2">
Địa chỉ cụ thể <span class="text-red-500">*</span>
</label>
<textarea id="addressDetail"
class="form-textarea w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition resize-none"
rows="3"
placeholder="Số nhà, tên đường, khu vực..."
required></textarea>
<p class="text-xs text-gray-500 mt-1">Ví dụ: 123 Nguyễn Huệ, Khu phố 5</p>
</div>
</div>
<!-- Default Address Option -->
<div class="bg-white rounded-lg shadow-sm p-4 mb-4">
<label class="flex items-center cursor-pointer">
<input type="checkbox"
id="isDefault"
class="form-checkbox h-5 w-5 text-blue-600 rounded border-gray-300 focus:ring-2 focus:ring-blue-500">
<span class="ml-3 text-sm font-medium text-gray-900">Đặt làm địa chỉ mặc định</span>
</label>
<p class="text-xs text-gray-500 mt-2 ml-8">
Địa chỉ này sẽ được sử dụng làm mặc định khi đặt hàng
</p>
</div>
<!-- Info Note -->
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-4">
<div class="flex gap-3">
<i class="fas fa-info-circle text-blue-600 text-lg flex-shrink-0 mt-0.5"></i>
<div class="text-sm text-blue-800">
<strong>Lưu ý:</strong> Vui lòng kiểm tra kỹ thông tin địa chỉ để đảm bảo giao hàng chính xác.
Bạn có thể chỉnh sửa hoặc xóa địa chỉ này sau khi lưu.
</div>
</div>
</div>
</form>
</div>
<!-- Sticky Footer with Save Button -->
<div class="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 shadow-lg z-50">
<div class="max-w-3xl mx-auto px-4 py-4">
<button type="submit"
form="addressForm"
id="saveBtn"
class="w-full bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800 text-white font-semibold py-4 px-6 rounded-lg shadow-lg transition-all duration-200 hover:shadow-xl hover:-translate-y-0.5 flex items-center justify-center gap-2">
<i class="fas fa-save"></i>
<span>Lưu địa chỉ</span>
</button>
</div>
</div>
</div>
<style>
/* Custom form styles */
.form-input:focus,
.form-select:focus,
.form-textarea:focus {
outline: none;
}
.form-select {
background-image: none;
}
.form-checkbox:checked {
background-color: #2563eb;
/*border-color: #2563eb;*/
}
/* Disabled state */
.form-select:disabled {
background-color: #f3f4f6;
cursor: not-allowed;
}
/* Animation */
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.form-group {
animation: slideDown 0.3s ease-out;
}
</style>
<script>
// Address data structure (simulated - in real app this comes from API)
const addressData = {
hanoi: {
name: "Hà Nội",
districts: {
"hoan-kiem": {
name: "Hoàn Kiếm",
wards: ["Hàng Bạc", "Hàng Bài", "Hàng Bồ", "Hàng Đào", "Hàng Gai"]
},
"ba-dinh": {
name: "Ba Đình",
wards: ["Điện Biên", "Đội Cấn", "Giảng Võ", "Kim Mã", "Ngọc Hà"]
},
"dong-da": {
name: "Đống Đa",
wards: ["Cát Linh", "Hàng Bột", "Khâm Thiên", "Láng Hạ", "Ô Chợ Dừa"]
},
"cau-giay": {
name: "Cầu Giấy",
wards: ["Dịch Vọng", "Mai Dịch", "Nghĩa Đô", "Quan Hoa", "Yên Hòa"]
}
}
},
hcm: {
name: "TP. Hồ Chí Minh",
districts: {
"quan-1": {
name: "Quận 1",
wards: ["Bến Nghé", "Bến Thành", "Cô Giang", "Đa Kao", "Nguyễn Thái Bình"]
},
"quan-3": {
name: "Quận 3",
wards: ["Võ Thị Sáu", "Phường 1", "Phường 2", "Phường 3", "Phường 4"]
},
"quan-5": {
name: "Quận 5",
wards: ["Phường 1", "Phường 2", "Phường 3", "Phường 4", "Phường 5"]
},
"quan-7": {
name: "Quận 7",
wards: ["Tân Phong", "Tân Phú", "Tân Quy", "Tân Thuận Đông", "Tân Thuận Tây"]
},
"binh-thanh": {
name: "Bình Thạnh",
wards: ["Phường 1", "Phường 2", "Phường 3", "Phường 5", "Phường 7"]
}
}
},
danang: {
name: "Đà Nẵng",
districts: {
"hai-chau": {
name: "Hải Châu",
wards: ["Hải Châu 1", "Hải Châu 2", "Nam Dương", "Phước Ninh", "Thạch Thang"]
},
"thanh-khe": {
name: "Thanh Khê",
wards: ["An Khê", "Chính Gián", "Tam Thuận", "Tân Chính", "Thạc Gián"]
},
"son-tra": {
name: "Sơn Trà",
wards: ["An Hải Bắc", "An Hải Đông", "Mân Thái", "Nại Hiên Đông", "Phước Mỹ"]
}
}
}
};
// Update districts when province changes
function updateDistricts() {
const provinceSelect = document.getElementById('province');
const districtSelect = document.getElementById('district');
const wardSelect = document.getElementById('ward');
const selectedProvince = provinceSelect.value;
// Reset district and ward
districtSelect.innerHTML = '<option value="">-- Chọn Quận/Huyện --</option>';
wardSelect.innerHTML = '<option value="">-- Chọn Phường/Xã --</option>';
wardSelect.disabled = true;
if (selectedProvince && addressData[selectedProvince]) {
const districts = addressData[selectedProvince].districts;
// Enable district select
districtSelect.disabled = false;
// Populate districts
Object.keys(districts).forEach(districtKey => {
const option = document.createElement('option');
option.value = districtKey;
option.textContent = districts[districtKey].name;
districtSelect.appendChild(option);
});
} else {
districtSelect.disabled = true;
}
}
// Update wards when district changes
function updateWards() {
const provinceSelect = document.getElementById('province');
const districtSelect = document.getElementById('district');
const wardSelect = document.getElementById('ward');
const selectedProvince = provinceSelect.value;
const selectedDistrict = districtSelect.value;
// Reset ward
wardSelect.innerHTML = '<option value="">-- Chọn Phường/Xã --</option>';
if (selectedProvince && selectedDistrict && addressData[selectedProvince]) {
const district = addressData[selectedProvince].districts[selectedDistrict];
if (district && district.wards) {
// Enable ward select
wardSelect.disabled = false;
// Populate wards
district.wards.forEach(ward => {
const option = document.createElement('option');
option.value = ward.toLowerCase().replace(/\s+/g, '-');
option.textContent = ward;
wardSelect.appendChild(option);
});
}
} else {
wardSelect.disabled = true;
}
}
// Handle form submission
function handleSubmit(event) {
event.preventDefault();
// Get form values
const formData = {
fullName: document.getElementById('fullName').value,
phone: document.getElementById('phone').value,
province: document.getElementById('province').value,
provinceName: document.getElementById('province').selectedOptions[0].text,
district: document.getElementById('district').value,
districtName: document.getElementById('district').selectedOptions[0].text,
ward: document.getElementById('ward').value,
wardName: document.getElementById('ward').selectedOptions[0].text,
addressDetail: document.getElementById('addressDetail').value,
isDefault: document.getElementById('isDefault').checked
};
// Validate
if (!formData.province || !formData.district || !formData.ward) {
showToast('Vui lòng chọn đầy đủ Tỉnh/Thành phố, Quận/Huyện, Phường/Xã', 'error');
return;
}
// Show loading
const saveBtn = document.getElementById('saveBtn');
const originalContent = saveBtn.innerHTML;
saveBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> <span>Đang lưu...</span>';
saveBtn.disabled = true;
// Simulate API call
setTimeout(() => {
// Save to localStorage (simulated)
let addresses = JSON.parse(localStorage.getItem('savedAddresses') || '[]');
// If this is default, remove default from others
if (formData.isDefault) {
addresses = addresses.map(addr => ({...addr, isDefault: false}));
}
// Add new address
addresses.push({
id: Date.now(),
...formData,
createdAt: new Date().toISOString()
});
localStorage.setItem('savedAddresses', JSON.stringify(addresses));
// Reset button
saveBtn.innerHTML = originalContent;
saveBtn.disabled = false;
// Show success and redirect
showToast('Đã lưu địa chỉ thành công!', 'success');
setTimeout(() => {
window.location.href = 'addresses.html';
}, 1000);
}, 1500);
}
// Toast notification
function showToast(message, type = 'success') {
const colors = {
success: '#10b981',
error: '#ef4444',
warning: '#f59e0b',
info: '#3b82f6'
};
const icons = {
success: 'fa-check-circle',
error: 'fa-exclamation-circle',
warning: 'fa-exclamation-triangle',
info: 'fa-info-circle'
};
const toast = document.createElement('div');
toast.innerHTML = `
<i class="fas ${icons[type]}"></i>
<span>${message}</span>
`;
toast.style.cssText = `
position: fixed;
top: 80px;
left: 50%;
transform: translateX(-50%);
background: ${colors[type]};
color: white;
padding: 12px 24px;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10000;
display: flex;
align-items: center;
gap: 8px;
animation: slideDown 0.3s ease;
max-width: 90%;
`;
document.body.appendChild(toast);
setTimeout(() => {
toast.style.animation = 'slideUp 0.3s ease';
setTimeout(() => {
document.body.removeChild(toast);
}, 300);
}, 3000);
}
// Add animation styles
const style = document.createElement('style');
style.textContent = `
@keyframes slideDown {
from {
opacity: 0;
transform: translate(-50%, -20px);
}
to {
opacity: 1;
transform: translate(-50%, 0);
}
}
@keyframes slideUp {
from {
opacity: 1;
transform: translate(-50%, 0);
}
to {
opacity: 0;
transform: translate(-50%, -20px);
}
}
`;
document.head.appendChild(style);
</script>
</body>
</html>