This commit is contained in:
Phuoc Nguyen
2025-11-14 11:50:40 +07:00
parent 0093b62c29
commit 4738553d2e
6 changed files with 1270 additions and 432 deletions

View File

@@ -29,8 +29,8 @@
<div class="product-detail-content">
<!-- Image Gallery Section -->
<div class="product-gallery-section">
<div class="main-image-container" >
<img id="mainImage" onclick="openLightbox(0)" src="https://www.eurotile.vn/pictures/catalog/product/0-gachkholon/cat-tuong/CAT-S01G-1.jpg" alt="Gạch Eurotile MỘC LAM E03" class="main-product-image">
<div class="main-image-container" onclick="openLightbox(0)">
<img id="mainImage" src="https://placehold.co/400x400/F5F5F5/005B9A/png?text=Gạch+Eurotile+MỘC+LAM+E03" alt="Gạch Eurotile MỘC LAM E03" class="main-product-image">
<!-- 360° Button overlay -->
<button class="view-360-btn-overlay" onclick="view360Product()" title="Xem sản phẩm 360°">
@@ -50,32 +50,32 @@
<!-- Thumbnail row -->
<div class="thumbnail-gallery">
<div class="thumbnail active" onclick="changeImage(0, this)">
<img src="https://www.eurotile.vn/pictures/catalog/product/0-gachkholon/cat-tuong/CAT-S01G-1.jpg" alt="Thumbnail 1">
<img src="https://placehold.co/80x80/F5F5F5/005B9A/png?text=1" alt="Thumbnail 1">
</div>
<div class="thumbnail" onclick="changeImage(1, this)">
<img src="https://www.eurotile.vn/pictures/catalog/product/0-gachkholon/cat-tuong/CAT-S01G-2.jpg" alt="Thumbnail 2">
<img src="https://placehold.co/80x80/E8E8E8/005B9A/png?text=2" alt="Thumbnail 2">
</div>
<div class="thumbnail" onclick="changeImage(2, this)">
<img src="https://www.eurotile.vn/pictures/catalog/product/0-gachkholon/cat-tuong/CAT-S01G-3.jpg" alt="Thumbnail 3">
<img src="https://placehold.co/80x80/DDDDDD/005B9A/png?text=3" alt="Thumbnail 3">
</div>
<div class="thumbnail" onclick="changeImage(3, this)">
<img src="https://www.eurotile.vn/pictures/catalog/product/0-gachkholon/cat-tuong/CAT-S01G-4.jpg" alt="Thumbnail 4">
<img src="https://placehold.co/80x80/D2D2D2/005B9A/png?text=4" alt="Thumbnail 4">
</div>
</div>
</div>
<!-- Product Information Section -->
<div class="product-info-section">
<div class="product-sku">SKU: CAT S01G</div>
<h1 class="product-title">Gạch Cát Tường 1200x1200</h1>
<div class="product-sku">SKU: ET-ML-E03-60x60</div>
<h1 class="product-title">Gạch Eurotile MỘC LAM E03</h1>
<div class="product-pricing">
<span class="current-price">285.000 VND/m²</span>
<!--<span class="original-price">320.000 VND/m²</span>
<span class="discount-badge">-11%</span>-->
<span class="original-price">320.000 VND/m²</span>
<span class="discount-badge">-11%</span>
</div>
<!-- Rating & Reviews -->
<!--<div class="rating-section">
<!-- Rating & Reviews -->
<div class="rating-section">
<div class="rating-stars">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
@@ -84,57 +84,17 @@
<i class="fas fa-star-half-alt"></i>
</div>
<span class="rating-text">4.8 (125 đánh giá)</span>
</div>-->
<div class="quick-info">
<div class="info-item">
<!--<i class="fas fa-cube info-icon"></i>-->
<i class="fas fa-expand info-icon"></i>
<div class="info-label">Kích thước</div>
<div class="info-value">1200x1200</div>
</div>
<div class="info-item">
<i class="fas fa-cube info-icon"></i>
<div class="info-label">Đóng gói</div>
<div class="info-value">2 viên/thùng</div>
</div>
<div class="info-item">
<i class="fas fa-truck info-icon"></i>
<!--<i class="fas fa-box-open info-icon"></i>
<!--<i class="fas fa-pallet info-icon"></i>-->
<div class="info-label">Giao hàng</div>
<div class="info-value">2-3 Ngày</div>
</div>
</div>
</div>
<!-- Product Tabs Section -->
<div class="product-tabs-section">
<div class="tab-navigation">
<!--<button class="tab-button" onclick="switchTab('description', this)">Mô tả</button>-->
<button class="tab-button active" onclick="switchTab('specifications', this)">Thông số</button>
<button class="tab-button" onclick="switchTab('reviews', this)">Đánh giá</button>
</div>
<!-- Tab Contents -->
<!--<div class="tab-content" id="description">
<div class="tab-content-wrapper">
<h3>Bộ sưu tập Mộc Lam</h3>
<p>Gạch granite Eurotile MỘC LAM E03 lấy cảm hứng từ vẻ đẹp tự nhiên của gỗ tự nhiên, mang đến không gian ấm cúng và gần gũi. Với bề mặt có texture tinh tế, sản phẩm tạo nên những đường vân gỗ tự nhiên chân thực.</p>
<h4>Đặc điểm nổi bật:</h4>
<ul class="feature-list">
<li><i class="fas fa-check"></i>Bề mặt chống trầy xước cao</li>
<li><i class="fas fa-check"></i>Khả năng chống thấm nước tốt</li>
<li><i class="fas fa-check"></i>Màu sắc bền đẹp theo thời gian</li>
<li><i class="fas fa-check"></i>Dễ dàng vệ sinh và bảo trì</li>
<li><i class="fas fa-check"></i>Thân thiện với môi trường</li>
</ul>
<h4>Ứng dụng:</h4>
<p>Phù hợp cho phòng khách, phòng ngủ, hành lang, văn phòng và các không gian thương mại. Đặc biệt phù hợp với phong cách nội thất hiện đại, tối giản và Scandinavian.</p>
</div>
</div>-->
<div class="tab-content active" id="specifications">
<div class="specifications-table">
<div class="spec-row">
@@ -149,6 +109,10 @@
<div class="spec-label">Bề mặt</div>
<div class="spec-value">Matt (Nhám)</div>
</div>
<div class="spec-row">
<div class="spec-label">Loại men</div>
<div class="spec-value">Granite kỹ thuật số</div>
</div>
<div class="spec-row">
<div class="spec-label">Độ hấp thụ nước</div>
<div class="spec-value">< 0.5%</div>
@@ -161,6 +125,14 @@
<div class="spec-label">Chức năng</div>
<div class="spec-value">Lát nền, Ốp tường</div>
</div>
<div class="spec-row">
<div class="spec-label">Xuất xứ</div>
<div class="spec-value">Việt Nam</div>
</div>
<div class="spec-row">
<div class="spec-label">Bảo hành</div>
<div class="spec-value">15 năm</div>
</div>
<div class="spec-row">
<div class="spec-label">Tiêu chuẩn</div>
<div class="spec-value">TCVN 9081:2012, ISO 13006</div>
@@ -169,60 +141,167 @@
</div>
<div class="tab-content" id="reviews">
<!-- Phần 1: Tổng quan Xếp hạng -->
<div class="reviews-summary">
<div class="rating-overview">
<div class="rating-score">4.8</div>
<div class="rating-score-large">4.5</div>
<div class="rating-details">
<div class="rating-stars-large">
<div class="rating-stars-display">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<div class="rating-count">125 đánh giá</div>
<div class="rating-count-text">từ 23 đánh giá</div>
</div>
</div>
<!-- Star Distribution Bars -->
<!-- <div class="star-distribution">
<div class="distribution-row">
<div class="stars-label">5 <i class="fas fa-star"></i></div>
<div class="distribution-bar-container">
<div class="distribution-bar" style="width: 80%;"></div>
</div>
<div class="distribution-percent">80%</div>
</div>
<div class="distribution-row">
<div class="stars-label">4 <i class="fas fa-star"></i></div>
<div class="distribution-bar-container">
<div class="distribution-bar" style="width: 15%;"></div>
</div>
<div class="distribution-percent">15%</div>
</div>
<div class="distribution-row">
<div class="stars-label">3 <i class="fas fa-star"></i></div>
<div class="distribution-bar-container">
<div class="distribution-bar" style="width: 5%;"></div>
</div>
<div class="distribution-percent">5%</div>
</div>
<div class="distribution-row">
<div class="stars-label">2 <i class="fas fa-star"></i></div>
<div class="distribution-bar-container">
<div class="distribution-bar" style="width: 0%;"></div>
</div>
<div class="distribution-percent">0%</div>
</div>
<div class="distribution-row">
<div class="stars-label">1 <i class="fas fa-star"></i></div>
<div class="distribution-bar-container">
<div class="distribution-bar" style="width: 0%;"></div>
</div>
<div class="distribution-percent">0%</div>
</div>
</div>-->
</div>
<!-- Phần 2: Nút "Viết đánh giá" (Hiển thị nếu chưa có đánh giá pending) -->
<div class="write-review-section" id="writeReviewSection">
<button class="btn-write-review" onclick="goToWriteReview()">
<i class="fas fa-edit"></i>
Viết đánh giá của bạn
</button>
</div>
<!-- Phần 3: Card đánh giá đang chờ duyệt (Ẩn mặc định) -->
<div class="pending-review-notice" id="pendingReviewNotice" style="display: none;">
<div class="review-item pending-review-item">
<div class="pending-badge">
<i class="fas fa-clock"></i>
Đang chờ duyệt
</div>
<div class="reviewer-info">
<div class="reviewer-avatar">
<i class="fas fa-user"></i>
</div>
<div class="reviewer-details">
<div class="reviewer-name">Nguyễn Văn A (Bạn)</div>
<div class="review-date">Hôm nay</div>
</div>
</div>
<div class="review-rating">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
</div>
<div class="review-title">Sản phẩm rất tốt, giao hàng nhanh</div>
<p class="review-text">Chất lượng gạch tuyệt vời, màu sắc đẹp và đúng như mô tả. Đội ngũ giao hàng chuyên nghiệp. Sẽ ủng hộ lâu dài!</p>
<div class="pending-review-note">
<i class="fas fa-info-circle"></i>
Đánh giá của bạn sẽ được hiển thị sau khi Admin xem xét và phê duyệt.
</div>
</div>
</div>
<div class="review-item">
<div class="reviewer-info">
<div class="reviewer-avatar">
<i class="fas fa-user"></i>
</div>
<div class="reviewer-details">
<div class="reviewer-name">Nguyễn Văn A</div>
<div class="review-date">2 tuần trước</div>
</div>
</div>
<div class="review-rating">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
</div>
<p class="review-text">Sản phẩm chất lượng tốt, màu sắc đẹp và dễ lắp đặt. Rất hài lòng với lựa chọn này cho ngôi nhà của gia đình.</p>
</div>
<!-- Phần 4: Danh sách đánh giá đã duyệt -->
<div class="reviews-list">
<h4 class="reviews-list-title">Đánh giá từ khách hàng</h4>
<div class="review-item">
<div class="reviewer-info">
<div class="reviewer-avatar">
<i class="fas fa-user"></i>
<div class="review-item">
<div class="reviewer-info">
<div class="reviewer-avatar">
<i class="fas fa-user"></i>
</div>
<div class="reviewer-details">
<div class="reviewer-name">Trần Văn B</div>
<div class="review-date">2 tuần trước</div>
</div>
</div>
<div class="reviewer-details">
<div class="reviewer-name">Trần Thị B</div>
<div class="review-date">1 tháng trước</div>
<div class="review-rating">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
</div>
<div class="review-title">Chất lượng xuất sắc!</div>
<p class="review-text">Sản phẩm chất lượng tốt, màu sắc đẹp và dễ lắp đặt. Rất hài lòng với lựa chọn này cho ngôi nhà của gia đình. Giao hàng nhanh và đóng gói cẩn thận.</p>
</div>
<div class="review-rating">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="far fa-star"></i>
<div class="review-item">
<div class="reviewer-info">
<div class="reviewer-avatar">
<i class="fas fa-user"></i>
</div>
<div class="reviewer-details">
<div class="reviewer-name">Lê Thị C</div>
<div class="review-date">1 tháng trước</div>
</div>
</div>
<div class="review-rating">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="far fa-star"></i>
</div>
<div class="review-title">Đẹp và chất lượng tốt</div>
<p class="review-text">Gạch đẹp, vân gỗ rất chân thực. Giao hàng nhanh chóng và đóng gói cẩn thận. Giá cả hợp lý so với chất lượng.</p>
</div>
<div class="review-item">
<div class="reviewer-info">
<div class="reviewer-avatar">
<i class="fas fa-user"></i>
</div>
<div class="reviewer-details">
<div class="reviewer-name">Phạm Minh D</div>
<div class="review-date">2 tháng trước</div>
</div>
</div>
<div class="review-rating">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
</div>
<p class="review-text">Sản phẩm tốt, đúng mô tả. Tư vấn nhiệt tình. Sẽ giới thiệu cho bạn bè.</p>
</div>
<p class="review-text">Gạch đẹp, vân gỗ rất chân thực. Giao hàng nhanh chóng và đóng gói cẩn thận.</p>
</div>
</div>
</div>
@@ -230,24 +309,9 @@
<!-- Sticky Action Bar -->
<div class="sticky-action-bar">
<!--<div class="quantity-controls">
<button class="qty-btn" onclick="decreaseQuantity()" id="decreaseBtn">
<i class="fas fa-minus"></i>
</button>
<input type="number" class="qty-input" value="1" min="1" id="quantityInput" onchange="updateQuantity()">
<label class="quantity-label">(m²)</label>
<button class="qty-btn" onclick="increaseQuantity()" id="increaseBtn">
<i class="fas fa-plus"></i>
</button>
</div>
<div class="conversion-text" id="conversionText">
Tương đương: 3 viên / 1.08 m²
</div>-->
<div class="quantity-section">
<label class="quantity-label">Số lượng (m²)</label>
<div class="quantity-controls" style="width: 142px;">
<div class="quantity-controls">
<button class="qty-btn" onclick="decreaseQuantity()" id="decreaseBtn">
<i class="fas fa-minus"></i>
</button>
@@ -257,9 +321,9 @@
</button>
</div>
<div class="conversion-text" id="conversionText">
Tương đương: 3 viên / 1.08 m²
</div>
Tương đương: 3 hộp / 12 viên
</div>
</div>
<button class="add-to-cart-btn" onclick="addToCart()">
<i class="fas fa-shopping-cart"></i>
<span>Thêm vào giỏ hàng</span>
@@ -679,6 +743,184 @@
color: var(--text-dark);
}
/* Rating Overview - Enhanced */
.rating-score-large {
font-size: 48px;
font-weight: 700;
color: var(--primary-blue);
line-height: 1;
}
.rating-stars-display {
display: flex;
gap: 4px;
margin-bottom: 6px;
}
.rating-stars-display i {
color: #ffc107;
font-size: 20px;
}
.rating-count-text {
font-size: 14px;
color: var(--text-light);
}
/* Star Distribution */
.star-distribution {
margin-top: 20px;
}
.distribution-row {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
}
.stars-label {
font-size: 14px;
font-weight: 500;
color: var(--text-dark);
min-width: 40px;
display: flex;
align-items: center;
gap: 4px;
}
.stars-label i {
color: #ffc107;
font-size: 12px;
}
.distribution-bar-container {
flex: 1;
height: 8px;
background: var(--border-color);
border-radius: 4px;
overflow: hidden;
}
.distribution-bar {
height: 100%;
background: linear-gradient(90deg, #ffc107 0%, #ff9800 100%);
border-radius: 4px;
transition: width 0.3s ease;
}
.distribution-percent {
font-size: 13px;
font-weight: 500;
color: var(--text-light);
min-width: 40px;
text-align: right;
}
/* Write Review Section */
.write-review-section {
margin-bottom: 24px;
/* padding: 20px;*/
/* background: var(--background-gray); */
border-radius: 12px;
text-align: center;
}
.btn-write-review {
background: var(--primary-blue);
color: white;
border: none;
padding: 14px 28px;
border-radius: 8px;
font-size: 15px;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 8px;
cursor: pointer;
transition: background 0.3s ease;
}
.btn-write-review:hover {
background: #004578;
}
.btn-write-review i {
font-size: 16px;
}
/* Pending Review Notice */
.pending-review-notice {
margin-bottom: 24px;
}
.pending-review-item {
background: #fffbf0;
border: 2px solid #ffc107;
border-radius: 12px;
padding: 16px;
position: relative;
}
.pending-badge {
position: absolute;
top: 12px;
right: 12px;
background: #ffc107;
color: #856404;
padding: 6px 12px;
border-radius: 16px;
font-size: 12px;
font-weight: 600;
display: flex;
align-items: center;
gap: 4px;
}
.pending-badge i {
font-size: 11px;
}
.review-title {
font-size: 15px;
font-weight: 600;
color: var(--text-dark);
margin-bottom: 8px;
}
.pending-review-note {
margin-top: 12px;
padding: 12px;
background: rgba(255, 193, 7, 0.1);
border-left: 3px solid #ffc107;
border-radius: 6px;
font-size: 13px;
color: #856404;
display: flex;
align-items: flex-start;
gap: 8px;
line-height: 1.5;
}
.pending-review-note i {
margin-top: 2px;
flex-shrink: 0;
}
/* Reviews List */
.reviews-list {
margin-top: 24px;
}
.reviews-list-title {
font-size: 16px;
font-weight: 600;
color: var(--text-dark);
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 2px solid var(--border-color);
}
/* Sticky Action Bar */
.sticky-action-bar {
position: fixed;
@@ -695,14 +937,6 @@
z-index: 100;
}
/*.quantity-controls {
display: flex;
align-items: center;
border: 1px solid var(--border-color);
border-radius: 8px;
overflow: hidden;
}*/
.quantity-section {
display: flex;
flex-direction: column;
@@ -715,6 +949,13 @@
font-weight: 500;
}
.conversion-text {
font-size: 11px;
color: var(--text-muted);
margin-top: 4px;
text-align: center;
}
.quantity-controls {
display: flex;
align-items: center;
@@ -722,6 +963,7 @@
border-radius: 8px;
overflow: hidden;
}
.qty-btn {
width: 40px;
height: 40px;
@@ -935,20 +1177,6 @@
margin: 0 10px;
}
}
.quantity-label {
font-size: 12px;
color: var(--text-muted);
font-weight: 500;
}
.conversion-text {
font-size: 11px;
color: var(--text-muted);
margin-top: 4px;
text-align: center;
}
</style>
<script>
@@ -960,17 +1188,17 @@
let touchEndX = 0;
const images = [
"https://www.eurotile.vn/pictures/catalog/product/0-gachkholon/cat-tuong/CAT-S01G-1.jpg",
"https://www.eurotile.vn/pictures/catalog/product/0-gachkholon/cat-tuong/CAT-S01G-2.jpg",
"https://www.eurotile.vn/pictures/catalog/product/0-gachkholon/cat-tuong/CAT-S01G-3.jpg",
"https://www.eurotile.vn/pictures/catalog/product/0-gachkholon/cat-tuong/CAT-S01G-4.jpg"
"https://placehold.co/400x400/F5F5F5/005B9A/png?text=Gạch+Eurotile+MỘC+LAM+E03",
"https://placehold.co/400x400/E8E8E8/005B9A/png?text=Chi+tiết+texture",
"https://placehold.co/400x400/DDDDDD/005B9A/png?text=Ứng+dụng+thực+tế",
"https://placehold.co/400x400/D2D2D2/005B9A/png?text=Góc+độ+khác"
];
const imageCaptions = [
"Face A",
"Face B",
"Face C",
"Face D"
"Ảnh phối cảnh dòng Mộc Lam với texture gỗ tự nhiên chân thực",
"Chi tiết texture bề mặt với độ nhám tinh tế, chống trượt an toàn",
"Ứng dụng thực tế trong không gian phòng khách hiện đại",
"Góc độ khác cho thấy độ bền màu và chất lượng sản phẩm"
];
function changeImage(index, thumbnail) {
@@ -1018,11 +1246,21 @@
if (newQuantity >= 1) {
quantity = newQuantity;
updateQuantityButtons();
updateConversion();
} else {
input.value = quantity;
}
}
function updateConversion() {
// Example conversion: each m² = 0.36 boxes, each box = 4 pieces
const boxes = Math.ceil(quantity / 2.78); // Round up for boxes needed
const pieces = boxes * 4;
document.getElementById('conversionText').textContent =
`Tương đương: ${boxes} hộp / ${pieces} viên`;
}
function updateQuantityButtons() {
const decreaseBtn = document.getElementById('decreaseBtn');
decreaseBtn.disabled = quantity <= 1;
@@ -1113,7 +1351,7 @@
}
function view360Product() {
window.location.href = 'https://design.eurotile.vn/pub/tool/panorama/show?obsPlanId=3FO3H1VE59R5&locale=en_US&_gl=1*1udzqeo*_gcl_au*MTI3NjIxMzY1NS4xNzU5NzE2Mjg5';
window.location.href = 'product-view-360.html';
}
function addToCart() {
@@ -1209,9 +1447,55 @@
}
// Initialize
// Review Functions
function goToWriteReview() {
// Get product info from page
const productName = document.querySelector('.product-name').textContent;
const productImage = document.getElementById('mainImage').src;
// Store in localStorage for write-review page
localStorage.setItem('reviewProduct', JSON.stringify({
name: productName,
image: productImage,
id: 'ET-ML-E03' // In production, this would be dynamic
}));
// Navigate to write review page
window.location.href = 'write-review.html';
}
// Demo function to toggle pending review display
function togglePendingReview(hasPendingReview) {
const writeReviewSection = document.getElementById('writeReviewSection');
const pendingReviewNotice = document.getElementById('pendingReviewNotice');
if (hasPendingReview) {
writeReviewSection.style.display = 'none';
pendingReviewNotice.style.display = 'block';
} else {
writeReviewSection.style.display = 'block';
pendingReviewNotice.style.display = 'none';
}
}
// Check if user has pending review on page load
// In production, this would come from API
const userHasPendingReview = false; // Change to true to test pending review UI
document.addEventListener('DOMContentLoaded', function() {
updateQuantityButtons();
// Initialize review section
togglePendingReview(userHasPendingReview);
// Check if returning from review submission
const reviewSubmitted = localStorage.getItem('reviewJustSubmitted');
if (reviewSubmitted === 'true') {
// Show pending review
togglePendingReview(true);
localStorage.removeItem('reviewJustSubmitted');
}
// Close lightbox with Escape key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && document.getElementById('lightbox').classList.contains('active')) {
@@ -1237,15 +1521,6 @@
}
});
});
function updateConversion() {
// Example conversion: each m² = 0.36 boxes, each box = 4 pieces
const pieces = Math.ceil(quantity / 0.36); // Round up for boxes needed
const dientich = parseFloat((pieces * 0.36).toFixed(2));
document.getElementById('conversionText').textContent =
`Tương đương: ${pieces} viên / ${dientich}`;
}
</script>
</body>
</html>

522
html/write-review.html Normal file
View File

@@ -0,0 +1,522 @@
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Viết đánh giá sản phẩm - 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>
<div class="page-wrapper">
<!-- Header -->
<div class="header">
<a href="product-detail-1.html" class="back-button">
<i class="fas fa-arrow-left"></i>
</a>
<h1 class="header-title">Viết đánh giá sản phẩm</h1>
<div style="width: 40px;"></div>
</div>
<div class="container" style="padding-bottom: 120px;">
<!-- Product Card (Read-only) -->
<div class="product-review-card">
<img id="productImage"
src="https://images.unsplash.com/photo-1615971677499-5467cbab01c0?w=100&h=100&fit=crop"
alt="Product"
class="product-review-image">
<div class="product-review-info">
<h3 id="productName" class="product-review-name">Gạch Eurotile MỘC LAM E03</h3>
<p class="product-review-code">Mã: ET-ML-E03</p>
</div>
</div>
<!-- Review Form -->
<form id="reviewForm" onsubmit="submitReview(event)">
<!-- Rating Section (Required) -->
<div class="form-section">
<h3 class="section-title required-field">Xếp hạng của bạn</h3>
<p class="section-subtitle">Bấm vào ngôi sao để chọn đánh giá</p>
<div class="star-rating-selector" id="starRatingSelector">
<button type="button" class="star-btn" data-rating="1" onclick="selectRating(1)">
<i class="far fa-star"></i>
</button>
<button type="button" class="star-btn" data-rating="2" onclick="selectRating(2)">
<i class="far fa-star"></i>
</button>
<button type="button" class="star-btn" data-rating="3" onclick="selectRating(3)">
<i class="far fa-star"></i>
</button>
<button type="button" class="star-btn" data-rating="4" onclick="selectRating(4)">
<i class="far fa-star"></i>
</button>
<button type="button" class="star-btn" data-rating="5" onclick="selectRating(5)">
<i class="far fa-star"></i>
</button>
</div>
<div class="rating-label-container">
<span id="ratingLabel" class="rating-label">Chưa chọn đánh giá</span>
</div>
<input type="hidden" id="ratingValue" name="rating" required>
<div id="ratingError" class="error-message" style="display: none;">
Vui lòng chọn số sao đánh giá
</div>
</div>
<!-- Review Title (Optional) -->
<!--<div class="form-section">
<label for="reviewTitle" class="section-title">Tiêu đề đánh giá</label>
<input type="text"
id="reviewTitle"
name="title"
class="form-input"
placeholder="VD: Sản phẩm chất lượng tốt"
maxlength="100">
<div class="input-hint">Không bắt buộc • Tối đa 100 ký tự</div>
</div>-->
<!-- Review Content (Required) -->
<div class="form-section">
<label for="reviewContent" class="section-title required-field">Nội dung đánh giá</label>
<textarea id="reviewContent"
name="content"
class="form-textarea"
placeholder="Chia sẻ trải nghiệm của bạn về sản phẩm này..."
rows="6"
required
minlength="20"
maxlength="1000"></textarea>
<div class="textarea-counter">
<span id="charCount">0</span> / 1000 ký tự
</div>
<div id="contentError" class="error-message" style="display: none;">
Nội dung đánh giá phải có ít nhất 20 ký tự
</div>
</div>
<!-- Guidelines -->
<div class="review-guidelines">
<div class="guideline-header">
<i class="fas fa-lightbulb"></i>
<span>Gợi ý viết đánh giá tốt</span>
</div>
<ul class="guideline-list">
<li>Chia sẻ trải nghiệm thực tế của bạn về sản phẩm</li>
<li>Đề cập đến chất lượng, màu sắc, độ bền của sản phẩm</li>
<li>Nêu rõ điểm tốt và điểm chưa tốt (nếu có)</li>
<li>Tránh spam, nội dung không phù hợp hoặc vi phạm</li>
</ul>
</div>
<!-- Submit Button -->
<div class="fixed-bottom-action">
<button type="submit" class="btn-submit-review" id="submitBtn">
<i class="fas fa-paper-plane"></i>
Gửi đánh giá
</button>
</div>
</form>
</div>
</div>
<style>
:root {
--primary-blue: #005B9A;
--star-gold: #ffc107;
--star-selected: #ff9800;
--text-dark: #333;
--text-light: #666;
--text-muted: #999;
--border-color: #e0e0e0;
--background-gray: #f8f9fa;
--success-color: #28a745;
--danger-color: #dc3545;
--white: #ffffff;
}
/* Product Review Card */
.product-review-card {
display: flex;
align-items: center;
gap: 16px;
padding: 16px;
background: var(--white);
border: 2px solid var(--border-color);
border-radius: 12px;
margin-bottom: 24px;
}
.product-review-image {
width: 80px;
height: 80px;
border-radius: 8px;
object-fit: cover;
flex-shrink: 0;
}
.product-review-info {
flex: 1;
}
.product-review-name {
font-size: 16px;
font-weight: 600;
color: var(--text-dark);
margin: 0 0 6px 0;
line-height: 1.4;
}
.product-review-code {
font-size: 13px;
color: var(--text-muted);
margin: 0;
}
/* Form Sections */
.form-section {
margin-bottom: 28px;
}
.section-title {
font-size: 16px;
font-weight: 600;
color: var(--text-dark);
margin-bottom: 8px;
display: block;
}
.section-title.required-field::after {
content: ' *';
color: var(--danger-color);
}
.section-subtitle {
font-size: 14px;
color: var(--text-light);
margin-bottom: 16px;
}
/* Star Rating Selector */
.star-rating-selector {
display: flex;
gap: 12px;
justify-content: center;
margin-bottom: 12px;
}
.star-btn {
background: none;
border: none;
padding: 8px;
cursor: pointer;
transition: transform 0.2s ease;
}
.star-btn:hover {
transform: scale(1.1);
}
.star-btn:active {
transform: scale(0.95);
}
.star-btn i {
font-size: 36px;
color: var(--border-color);
transition: color 0.2s ease;
}
.star-btn:hover i {
color: var(--star-gold);
}
.star-btn.selected i {
color: var(--star-selected);
}
.star-btn.selected:hover i {
color: var(--star-gold);
}
/* Rating Label */
.rating-label-container {
text-align: center;
margin-top: 12px;
}
.rating-label {
font-size: 15px;
font-weight: 600;
color: var(--text-light);
padding: 8px 20px;
background: var(--background-gray);
border-radius: 20px;
display: inline-block;
}
.rating-label.selected {
color: var(--star-selected);
background: #fff3e0;
}
/* Form Inputs */
.form-input {
width: 100%;
padding: 14px 16px;
border: 2px solid var(--border-color);
border-radius: 8px;
font-size: 15px;
color: var(--text-dark);
transition: border-color 0.3s ease;
}
.form-input:focus {
outline: none;
border-color: var(--primary-blue);
}
.form-textarea {
width: 100%;
padding: 14px 16px;
border: 2px solid var(--border-color);
border-radius: 8px;
font-size: 15px;
color: var(--text-dark);
resize: vertical;
font-family: inherit;
line-height: 1.6;
transition: border-color 0.3s ease;
}
.form-textarea:focus {
outline: none;
border-color: var(--primary-blue);
}
/* Input Hints */
.input-hint {
font-size: 13px;
color: var(--text-muted);
margin-top: 6px;
}
.textarea-counter {
font-size: 13px;
color: var(--text-muted);
text-align: right;
margin-top: 6px;
}
/* Error Messages */
.error-message {
font-size: 13px;
color: var(--danger-color);
margin-top: 6px;
display: flex;
align-items: center;
gap: 4px;
}
.error-message::before {
content: '⚠';
font-size: 14px;
}
/* Guidelines */
.review-guidelines {
padding: 16px;
background: #f0f7ff;
border-left: 4px solid var(--primary-blue);
border-radius: 8px;
margin-bottom: 24px;
}
.guideline-header {
display: flex;
align-items: center;
gap: 8px;
font-size: 15px;
font-weight: 600;
color: var(--primary-blue);
margin-bottom: 12px;
}
.guideline-header i {
font-size: 16px;
}
.guideline-list {
margin: 0;
padding-left: 24px;
}
.guideline-list li {
font-size: 14px;
color: var(--text-dark);
line-height: 1.6;
margin-bottom: 6px;
}
/* Fixed Bottom Action */
.fixed-bottom-action {
position: fixed;
bottom: 70px;
left: 0;
right: 0;
padding: 16px;
background: var(--white);
border-top: 1px solid var(--border-color);
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
z-index: 100;
}
.btn-submit-review {
width: 100%;
background: var(--primary-blue);
color: white;
border: none;
padding: 16px 24px;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
cursor: pointer;
transition: all 0.3s ease;
}
.btn-submit-review:hover {
background: #004578;
}
.btn-submit-review:active {
transform: scale(0.98);
}
.btn-submit-review:disabled {
background: var(--border-color);
cursor: not-allowed;
}
.btn-submit-review i {
font-size: 16px;
}
</style>
<script>
let selectedRating = 0;
const ratingLabels = {
0: 'Chưa chọn đánh giá',
1: 'Rất không hài lòng',
2: 'Không hài lòng',
3: 'Bình thường',
4: 'Hài lòng',
5: 'Rất hài lòng'
};
// Load product info from localStorage
document.addEventListener('DOMContentLoaded', function() {
const productData = localStorage.getItem('reviewProduct');
if (productData) {
const product = JSON.parse(productData);
document.getElementById('productName').textContent = product.name;
document.getElementById('productImage').src = product.image;
}
// Character counter
const textarea = document.getElementById('reviewContent');
const charCount = document.getElementById('charCount');
textarea.addEventListener('input', function() {
const count = this.value.length;
charCount.textContent = count;
if (count < 20) {
charCount.style.color = 'var(--danger-color)';
} else {
charCount.style.color = 'var(--text-muted)';
}
});
});
// Star rating selection
function selectRating(rating) {
selectedRating = rating;
document.getElementById('ratingValue').value = rating;
document.getElementById('ratingError').style.display = 'none';
// Update star buttons
const starButtons = document.querySelectorAll('.star-btn');
starButtons.forEach((btn, index) => {
const icon = btn.querySelector('i');
if (index < rating) {
btn.classList.add('selected');
icon.classList.remove('far');
icon.classList.add('fas');
} else {
btn.classList.remove('selected');
icon.classList.remove('fas');
icon.classList.add('far');
}
});
// Update rating label
const ratingLabel = document.getElementById('ratingLabel');
ratingLabel.textContent = ratingLabels[rating];
ratingLabel.classList.add('selected');
}
// Form submission
function submitReview(event) {
event.preventDefault();
// Validation
let isValid = true;
// Check rating
if (selectedRating === 0) {
document.getElementById('ratingError').style.display = 'block';
isValid = false;
}
// Check content length
const content = document.getElementById('reviewContent').value;
if (content.length < 20) {
document.getElementById('contentError').style.display = 'block';
isValid = false;
} else {
document.getElementById('contentError').style.display = 'none';
}
if (!isValid) {
return false;
}
// Show loading state
const submitBtn = document.getElementById('submitBtn');
const originalText = submitBtn.innerHTML;
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Đang gửi...';
submitBtn.disabled = true;
// Simulate API call
setTimeout(() => {
// Set flag for product-detail page
localStorage.setItem('reviewJustSubmitted', 'true');
// Navigate to success page
window.location.href = 'review-submitted.html';
}, 1500);
return false;
}
</script>
</body>
</html>

View File

@@ -49,5 +49,7 @@
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
</dict>
</plist>

View File

@@ -193,6 +193,7 @@ class AuthRemoteDataSource {
'customer': 1,
},
'limit_page_length': 0,
'order_by': 'custom_line_no asc'
},
options: Options(
headers: {

View File

@@ -71,14 +71,50 @@ class _BusinessUnitSelectionPageState extends State<BusinessUnitSelectionPage> {
name: 'LPKD',
description: 'Đơn vị kinh doanh LPKD',
),
const BusinessUnit(
id: '2',
code: 'HSKD',
name: 'HSKD',
description: 'Đơn vị kinh doanh HSKD',
),
const BusinessUnit(
id: '3',
code: 'LPKD',
name: 'LPKD',
description: 'Đơn vị kinh doanh LPKD',
),
const BusinessUnit(
id: '2',
code: 'HSKD',
name: 'HSKD',
description: 'Đơn vị kinh doanh HSKD',
),
const BusinessUnit(
id: '3',
code: 'LPKD',
name: 'LPKD',
description: 'Đơn vị kinh doanh LPKD',
),
const BusinessUnit(
id: '2',
code: 'HSKD',
name: 'HSKD',
description: 'Đơn vị kinh doanh HSKD',
),
const BusinessUnit(
id: '3',
code: 'LPKD',
name: 'LPKD',
description: 'Đơn vị kinh doanh LPKD',
),
];
}
void _handleContinue() {
if (_selectedUnit == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Vui lòng chọn đơn vị kinh doanh'),
const SnackBar(
content: Text('Vui lòng chọn đơn vị kinh doanh'),
backgroundColor: AppColors.danger,
),
);
@@ -146,249 +182,252 @@ class _BusinessUnitSelectionPageState extends State<BusinessUnitSelectionPage> {
const SizedBox(width: AppSpacing.sm),
],
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(AppSpacing.lg),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Logo Section
Center(
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.primaryBlue, AppColors.lightBlue],
body: Padding(
padding: const EdgeInsets.all(AppSpacing.lg),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Logo Section
Center(
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.primaryBlue, AppColors.lightBlue],
),
borderRadius: BorderRadius.circular(20),
),
child: const Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'DBIZ',
style: TextStyle(
color: Colors.white,
fontSize: 32,
fontWeight: FontWeight.w700,
),
),
borderRadius: BorderRadius.circular(20),
),
child: const Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'DBIZ',
style: TextStyle(
color: Colors.white,
fontSize: 32,
fontWeight: FontWeight.w700,
),
),
Text(
'Worker App',
style: TextStyle(color: Colors.white, fontSize: 12),
),
],
),
Text(
'Worker App',
style: TextStyle(color: Colors.white, fontSize: 12),
),
],
),
),
),
const SizedBox(height: AppSpacing.xl),
const SizedBox(height: AppSpacing.xl),
// Welcome Message
const Text(
'Chọn đơn vị kinh doanh để tiếp tục',
textAlign: TextAlign.center,
style: TextStyle(color: AppColors.grey500, fontSize: 14),
// Welcome Message
const Text(
'Chọn đơn vị kinh doanh để tiếp tục',
textAlign: TextAlign.center,
style: TextStyle(color: AppColors.grey500, fontSize: 14),
),
const SizedBox(height: 40),
const Padding(
padding: EdgeInsets.symmetric(horizontal: AppSpacing.sm),
child: Text(
'Đơn vị kinh doanh',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.grey900,
),
),
),
const SizedBox(height: AppSpacing.md),
const SizedBox(height: 40),
// Business Unit Selection List
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Business Unit List Tiles
...(_availableUnits.asMap().entries.map((entry) {
final index = entry.key;
final unit = entry.value;
final isSelected = _selectedUnit?.id == unit.id;
final isFirst = index == 0;
final isLast = index == _availableUnits.length - 1;
// Business Unit Selection List
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.symmetric(horizontal: AppSpacing.sm),
child: Text(
'Đơn vị kinh doanh',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.grey900,
),
),
),
const SizedBox(height: AppSpacing.md),
// Business Unit List Tiles
...(_availableUnits.asMap().entries.map((entry) {
final index = entry.key;
final unit = entry.value;
final isSelected = _selectedUnit?.id == unit.id;
final isFirst = index == 0;
final isLast = index == _availableUnits.length - 1;
return Container(
margin: EdgeInsets.only(
bottom: isLast ? 0 : AppSpacing.xs,
),
decoration: BoxDecoration(
color: AppColors.white,
border: Border.all(
color: isSelected
? AppColors.primaryBlue
: AppColors.grey100,
width: isSelected ? 2 : 1,
return Container(
margin: EdgeInsets.only(
bottom: isLast ? 0 : AppSpacing.xs,
),
borderRadius: BorderRadius.vertical(
top: isFirst
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
bottom: isLast
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
),
boxShadow: isSelected
? [
BoxShadow(
color: AppColors.primaryBlue.withValues(
alpha: 0.1,
),
blurRadius: 8,
offset: const Offset(0, 2),
),
]
: null,
),
child: InkWell(
onTap: () {
setState(() {
_selectedUnit = unit;
});
},
borderRadius: BorderRadius.vertical(
top: isFirst
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
bottom: isLast
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.lg,
vertical: AppSpacing.md,
decoration: BoxDecoration(
color: AppColors.white,
border: Border.all(
color: isSelected
? AppColors.primaryBlue
: AppColors.grey100,
width: isSelected ? 2 : 1,
),
child: Row(
children: [
// Icon
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: isSelected
? AppColors.primaryBlue.withValues(
alpha: 0.1,
)
: AppColors.grey50,
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.business,
color: isSelected
? AppColors.primaryBlue
: AppColors.grey500,
size: 20,
),
),
const SizedBox(width: AppSpacing.md),
// Unit Name
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
unit.name,
style: TextStyle(
fontSize: 16,
fontWeight: isSelected
? FontWeight.w600
: FontWeight.w500,
color: isSelected
? AppColors.primaryBlue
: AppColors.grey900,
),
borderRadius: BorderRadius.vertical(
top: isFirst
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
bottom: isLast
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
),
boxShadow: isSelected
? [
BoxShadow(
color: AppColors.primaryBlue.withValues(
alpha: 0.1,
),
if (unit.description != null) ...[
const SizedBox(height: 2),
Text(
unit.description!,
style: const TextStyle(
fontSize: 12,
color: AppColors.grey500,
),
),
],
],
),
),
// Radio indicator
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
blurRadius: 8,
offset: const Offset(0, 2),
),
]
: null,
),
child: InkWell(
onTap: () {
setState(() {
_selectedUnit = unit;
});
},
borderRadius: BorderRadius.vertical(
top: isFirst
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
bottom: isLast
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.lg,
vertical: AppSpacing.md,
),
child: Row(
children: [
// Icon
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: isSelected
? AppColors.primaryBlue.withValues(
alpha: 0.1,
)
: AppColors.grey50,
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.business,
color: isSelected
? AppColors.primaryBlue
: AppColors.grey500,
width: 2,
size: 20,
),
color: isSelected
? AppColors.primaryBlue
: Colors.transparent,
),
child: isSelected
? const Icon(
Icons.circle,
size: 10,
color: AppColors.white,
)
: null,
),
],
const SizedBox(width: AppSpacing.md),
// Unit Name
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
unit.name,
style: TextStyle(
fontSize: 16,
fontWeight: isSelected
? FontWeight.w600
: FontWeight.w500,
color: isSelected
? AppColors.primaryBlue
: AppColors.grey900,
),
),
if (unit.description != null) ...[
const SizedBox(height: 2),
Text(
unit.description!,
style: const TextStyle(
fontSize: 12,
color: AppColors.grey500,
),
),
],
],
),
),
// Radio indicator
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: isSelected
? AppColors.primaryBlue
: AppColors.grey500,
width: 2,
),
color: isSelected
? AppColors.primaryBlue
: Colors.transparent,
),
child: isSelected
? const Icon(
Icons.circle,
size: 10,
color: AppColors.white,
)
: null,
),
],
),
),
),
),
);
}).toList()),
],
),
const SizedBox(height: AppSpacing.xl),
// Continue Button
SizedBox(
height: ButtonSpecs.height,
child: ElevatedButton(
onPressed: _handleContinue,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryBlue,
foregroundColor: Colors.white,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
ButtonSpecs.borderRadius,
),
),
),
child: const Text(
'Tiếp tục',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
);
}).toList()),
],
),
),
],
),
),
const SizedBox(height: AppSpacing.xl),
// Continue Button
SizedBox(
height: ButtonSpecs.height,
child: ElevatedButton(
onPressed: _handleContinue,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryBlue,
foregroundColor: Colors.white,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
ButtonSpecs.borderRadius,
),
),
),
child: const Text(
'Tiếp tục',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
),
),
const SizedBox(height: AppSpacing.xl),
],
),
),
);

View File

@@ -10,7 +10,6 @@ import 'package:intl/intl.dart';
import 'package:shimmer/shimmer.dart';
import 'package:worker/core/constants/ui_constants.dart';
import 'package:worker/core/theme/colors.dart';
import 'package:worker/features/favorites/presentation/providers/favorites_provider.dart';
import 'package:worker/features/products/domain/entities/product.dart';
import 'package:worker/generated/l10n/app_localizations.dart';
@@ -38,7 +37,7 @@ class ProductCard extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final l10n = AppLocalizations.of(context);
final isFavorited = ref.watch(isFavoriteProvider(product.productId));
// final isFavorited = ref.watch(isFavoriteProvider(product.productId));
return Card(
elevation: ProductCardSpecs.elevation,
@@ -135,64 +134,64 @@ class ProductCard extends ConsumerWidget {
),
// Favorite Button (bottom-left corner)
Positioned(
bottom: AppSpacing.sm,
left: AppSpacing.sm,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () async {
// Capture current state before toggle
final wasfavorited = isFavorited;
// Toggle favorite
await ref
.read(favoritesProvider.notifier)
.toggleFavorite(product.productId);
// Show feedback with correct message
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
wasfavorited
? 'Đã xóa khỏi yêu thích'
: 'Đã thêm vào yêu thích',
),
duration: const Duration(seconds: 1),
behavior: SnackBarBehavior.floating,
),
);
}
},
borderRadius: BorderRadius.circular(20),
child: Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: AppColors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.15),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
),
child: Icon(
isFavorited
? Icons.favorite
: Icons.favorite_border,
color: isFavorited
? AppColors.danger
: AppColors.grey500,
size: 20,
),
),
),
),
),
// Positioned(
// bottom: AppSpacing.sm,
// left: AppSpacing.sm,
// child: Material(
// color: Colors.transparent,
// child: InkWell(
// onTap: () async {
// // Capture current state before toggle
// final wasfavorited = isFavorited;
//
// // Toggle favorite
// await ref
// .read(favoritesProvider.notifier)
// .toggleFavorite(product.productId);
//
// // Show feedback with correct message
// if (context.mounted) {
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(
// content: Text(
// wasfavorited
// ? 'Đã xóa khỏi yêu thích'
// : 'Đã thêm vào yêu thích',
// ),
// duration: const Duration(seconds: 1),
// behavior: SnackBarBehavior.floating,
// ),
// );
// }
// },
// borderRadius: BorderRadius.circular(20),
// child: Container(
// width: 36,
// height: 36,
// decoration: BoxDecoration(
// color: AppColors.white,
// shape: BoxShape.circle,
// boxShadow: [
// BoxShadow(
// color: Colors.black.withValues(alpha: 0.15),
// blurRadius: 6,
// offset: const Offset(0, 2),
// ),
// ],
// ),
// child: Icon(
// isFavorited
// ? Icons.favorite
// : Icons.favorite_border,
// color: isFavorited
// ? AppColors.danger
// : AppColors.grey500,
// size: 20,
// ),
// ),
// ),
// ),
// ),
],
),
),