Compare commits
2 Commits
2f296ad8d3
...
4738553d2e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4738553d2e | ||
|
|
0093b62c29 |
@@ -29,8 +29,8 @@
|
|||||||
<div class="product-detail-content">
|
<div class="product-detail-content">
|
||||||
<!-- Image Gallery Section -->
|
<!-- Image Gallery Section -->
|
||||||
<div class="product-gallery-section">
|
<div class="product-gallery-section">
|
||||||
<div class="main-image-container" >
|
<div class="main-image-container" onclick="openLightbox(0)">
|
||||||
<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">
|
<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 -->
|
<!-- 360° Button overlay -->
|
||||||
<button class="view-360-btn-overlay" onclick="view360Product()" title="Xem sản phẩm 360°">
|
<button class="view-360-btn-overlay" onclick="view360Product()" title="Xem sản phẩm 360°">
|
||||||
@@ -50,32 +50,32 @@
|
|||||||
<!-- Thumbnail row -->
|
<!-- Thumbnail row -->
|
||||||
<div class="thumbnail-gallery">
|
<div class="thumbnail-gallery">
|
||||||
<div class="thumbnail active" onclick="changeImage(0, this)">
|
<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>
|
||||||
<div class="thumbnail" onclick="changeImage(1, this)">
|
<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>
|
||||||
<div class="thumbnail" onclick="changeImage(2, this)">
|
<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>
|
||||||
<div class="thumbnail" onclick="changeImage(3, this)">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Product Information Section -->
|
<!-- Product Information Section -->
|
||||||
<div class="product-info-section">
|
<div class="product-info-section">
|
||||||
<div class="product-sku">SKU: CAT S01G</div>
|
<div class="product-sku">SKU: ET-ML-E03-60x60</div>
|
||||||
<h1 class="product-title">Gạch Cát Tường 1200x1200</h1>
|
<h1 class="product-title">Gạch Eurotile MỘC LAM E03</h1>
|
||||||
|
|
||||||
<div class="product-pricing">
|
<div class="product-pricing">
|
||||||
<span class="current-price">285.000 VND/m²</span>
|
<span class="current-price">285.000 VND/m²</span>
|
||||||
<!--<span class="original-price">320.000 VND/m²</span>
|
<span class="original-price">320.000 VND/m²</span>
|
||||||
<span class="discount-badge">-11%</span>-->
|
<span class="discount-badge">-11%</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- Rating & Reviews -->
|
|
||||||
<!--<div class="rating-section">
|
<!-- Rating & Reviews -->
|
||||||
|
<div class="rating-section">
|
||||||
<div class="rating-stars">
|
<div class="rating-stars">
|
||||||
<i class="fas fa-star"></i>
|
<i class="fas fa-star"></i>
|
||||||
<i class="fas fa-star"></i>
|
<i class="fas fa-star"></i>
|
||||||
@@ -84,57 +84,17 @@
|
|||||||
<i class="fas fa-star-half-alt"></i>
|
<i class="fas fa-star-half-alt"></i>
|
||||||
</div>
|
</div>
|
||||||
<span class="rating-text">4.8 (125 đánh giá)</span>
|
<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>
|
||||||
<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>
|
</div>
|
||||||
|
|
||||||
<!-- Product Tabs Section -->
|
<!-- Product Tabs Section -->
|
||||||
<div class="product-tabs-section">
|
<div class="product-tabs-section">
|
||||||
<div class="tab-navigation">
|
<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 active" onclick="switchTab('specifications', this)">Thông số</button>
|
||||||
<button class="tab-button" onclick="switchTab('reviews', this)">Đánh giá</button>
|
<button class="tab-button" onclick="switchTab('reviews', this)">Đánh giá</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tab Contents -->
|
<!-- 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="tab-content active" id="specifications">
|
||||||
<div class="specifications-table">
|
<div class="specifications-table">
|
||||||
<div class="spec-row">
|
<div class="spec-row">
|
||||||
@@ -149,6 +109,10 @@
|
|||||||
<div class="spec-label">Bề mặt</div>
|
<div class="spec-label">Bề mặt</div>
|
||||||
<div class="spec-value">Matt (Nhám)</div>
|
<div class="spec-value">Matt (Nhám)</div>
|
||||||
</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-row">
|
||||||
<div class="spec-label">Độ hấp thụ nước</div>
|
<div class="spec-label">Độ hấp thụ nước</div>
|
||||||
<div class="spec-value">< 0.5%</div>
|
<div class="spec-value">< 0.5%</div>
|
||||||
@@ -161,6 +125,14 @@
|
|||||||
<div class="spec-label">Chức năng</div>
|
<div class="spec-label">Chức năng</div>
|
||||||
<div class="spec-value">Lát nền, Ốp tường</div>
|
<div class="spec-value">Lát nền, Ốp tường</div>
|
||||||
</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-row">
|
||||||
<div class="spec-label">Tiêu chuẩn</div>
|
<div class="spec-label">Tiêu chuẩn</div>
|
||||||
<div class="spec-value">TCVN 9081:2012, ISO 13006</div>
|
<div class="spec-value">TCVN 9081:2012, ISO 13006</div>
|
||||||
@@ -169,60 +141,167 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-content" id="reviews">
|
<div class="tab-content" id="reviews">
|
||||||
|
<!-- Phần 1: Tổng quan Xếp hạng -->
|
||||||
<div class="reviews-summary">
|
<div class="reviews-summary">
|
||||||
<div class="rating-overview">
|
<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-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"></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>
|
<i class="fas fa-star-half-alt"></i>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="review-item">
|
<!-- Phần 4: Danh sách đánh giá đã duyệt -->
|
||||||
<div class="reviewer-info">
|
<div class="reviews-list">
|
||||||
<div class="reviewer-avatar">
|
<h4 class="reviews-list-title">Đánh giá từ khách hàng</h4>
|
||||||
<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>
|
|
||||||
|
|
||||||
<div class="review-item">
|
<div class="review-item">
|
||||||
<div class="reviewer-info">
|
<div class="reviewer-info">
|
||||||
<div class="reviewer-avatar">
|
<div class="reviewer-avatar">
|
||||||
<i class="fas fa-user"></i>
|
<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>
|
||||||
<div class="reviewer-details">
|
<div class="review-rating">
|
||||||
<div class="reviewer-name">Trần Thị B</div>
|
<i class="fas fa-star"></i>
|
||||||
<div class="review-date">1 tháng trước</div>
|
<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>
|
||||||
|
<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>
|
||||||
<div class="review-rating">
|
|
||||||
<i class="fas fa-star"></i>
|
<div class="review-item">
|
||||||
<i class="fas fa-star"></i>
|
<div class="reviewer-info">
|
||||||
<i class="fas fa-star"></i>
|
<div class="reviewer-avatar">
|
||||||
<i class="fas fa-star"></i>
|
<i class="fas fa-user"></i>
|
||||||
<i class="far fa-star"></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>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -230,24 +309,9 @@
|
|||||||
|
|
||||||
<!-- Sticky Action Bar -->
|
<!-- Sticky Action Bar -->
|
||||||
<div class="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">
|
<div class="quantity-section">
|
||||||
<label class="quantity-label">Số lượng (m²)</label>
|
<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">
|
<button class="qty-btn" onclick="decreaseQuantity()" id="decreaseBtn">
|
||||||
<i class="fas fa-minus"></i>
|
<i class="fas fa-minus"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -257,9 +321,9 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="conversion-text" id="conversionText">
|
<div class="conversion-text" id="conversionText">
|
||||||
Tương đương: 3 viên / 1.08 m²
|
Tương đương: 3 hộp / 12 viên
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<button class="add-to-cart-btn" onclick="addToCart()">
|
<button class="add-to-cart-btn" onclick="addToCart()">
|
||||||
<i class="fas fa-shopping-cart"></i>
|
<i class="fas fa-shopping-cart"></i>
|
||||||
<span>Thêm vào giỏ hàng</span>
|
<span>Thêm vào giỏ hàng</span>
|
||||||
@@ -679,6 +743,184 @@
|
|||||||
color: var(--text-dark);
|
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 */
|
||||||
.sticky-action-bar {
|
.sticky-action-bar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -695,14 +937,6 @@
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*.quantity-controls {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
.quantity-section {
|
.quantity-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -715,6 +949,13 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.conversion-text {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-top: 4px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.quantity-controls {
|
.quantity-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -722,6 +963,7 @@
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qty-btn {
|
.qty-btn {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@@ -935,20 +1177,6 @@
|
|||||||
margin: 0 10px;
|
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>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -960,17 +1188,17 @@
|
|||||||
let touchEndX = 0;
|
let touchEndX = 0;
|
||||||
|
|
||||||
const images = [
|
const images = [
|
||||||
"https://www.eurotile.vn/pictures/catalog/product/0-gachkholon/cat-tuong/CAT-S01G-1.jpg",
|
"https://placehold.co/400x400/F5F5F5/005B9A/png?text=Gạch+Eurotile+MỘC+LAM+E03",
|
||||||
"https://www.eurotile.vn/pictures/catalog/product/0-gachkholon/cat-tuong/CAT-S01G-2.jpg",
|
"https://placehold.co/400x400/E8E8E8/005B9A/png?text=Chi+tiết+texture",
|
||||||
"https://www.eurotile.vn/pictures/catalog/product/0-gachkholon/cat-tuong/CAT-S01G-3.jpg",
|
"https://placehold.co/400x400/DDDDDD/005B9A/png?text=Ứng+dụng+thực+tế",
|
||||||
"https://www.eurotile.vn/pictures/catalog/product/0-gachkholon/cat-tuong/CAT-S01G-4.jpg"
|
"https://placehold.co/400x400/D2D2D2/005B9A/png?text=Góc+độ+khác"
|
||||||
];
|
];
|
||||||
|
|
||||||
const imageCaptions = [
|
const imageCaptions = [
|
||||||
"Face A",
|
"Ảnh phối cảnh dòng Mộc Lam với texture gỗ tự nhiên chân thực",
|
||||||
"Face B",
|
"Chi tiết texture bề mặt với độ nhám tinh tế, chống trượt an toàn",
|
||||||
"Face C",
|
"Ứng dụng thực tế trong không gian phòng khách hiện đại",
|
||||||
"Face D"
|
"Góc độ khác cho thấy độ bền màu và chất lượng sản phẩm"
|
||||||
];
|
];
|
||||||
|
|
||||||
function changeImage(index, thumbnail) {
|
function changeImage(index, thumbnail) {
|
||||||
@@ -1018,11 +1246,21 @@
|
|||||||
if (newQuantity >= 1) {
|
if (newQuantity >= 1) {
|
||||||
quantity = newQuantity;
|
quantity = newQuantity;
|
||||||
updateQuantityButtons();
|
updateQuantityButtons();
|
||||||
|
updateConversion();
|
||||||
} else {
|
} else {
|
||||||
input.value = quantity;
|
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() {
|
function updateQuantityButtons() {
|
||||||
const decreaseBtn = document.getElementById('decreaseBtn');
|
const decreaseBtn = document.getElementById('decreaseBtn');
|
||||||
decreaseBtn.disabled = quantity <= 1;
|
decreaseBtn.disabled = quantity <= 1;
|
||||||
@@ -1113,7 +1351,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function view360Product() {
|
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() {
|
function addToCart() {
|
||||||
@@ -1209,9 +1447,55 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize
|
// 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() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
updateQuantityButtons();
|
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
|
// Close lightbox with Escape key
|
||||||
document.addEventListener('keydown', function(e) {
|
document.addEventListener('keydown', function(e) {
|
||||||
if (e.key === 'Escape' && document.getElementById('lightbox').classList.contains('active')) {
|
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} m²`;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
522
html/write-review.html
Normal file
522
html/write-review.html
Normal 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>
|
||||||
@@ -176,7 +176,7 @@ SPEC CHECKSUMS:
|
|||||||
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
|
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
|
||||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||||
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49
|
||||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
||||||
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
|
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
|
||||||
@@ -196,7 +196,7 @@ SPEC CHECKSUMS:
|
|||||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||||
SDWebImage: 9f177d83116802728e122410fb25ad88f5c7608a
|
SDWebImage: 9f177d83116802728e122410fb25ad88f5c7608a
|
||||||
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
|
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
|
||||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||||
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
||||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>Worker</string>
|
<string>DBIZ Partner</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
@@ -49,5 +49,7 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ class AuthRemoteDataSource {
|
|||||||
'customer': 1,
|
'customer': 1,
|
||||||
},
|
},
|
||||||
'limit_page_length': 0,
|
'limit_page_length': 0,
|
||||||
|
'order_by': 'custom_line_no asc'
|
||||||
},
|
},
|
||||||
options: Options(
|
options: Options(
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -71,14 +71,50 @@ class _BusinessUnitSelectionPageState extends State<BusinessUnitSelectionPage> {
|
|||||||
name: 'LPKD',
|
name: 'LPKD',
|
||||||
description: 'Đơn vị kinh doanh 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() {
|
void _handleContinue() {
|
||||||
if (_selectedUnit == null) {
|
if (_selectedUnit == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
const SnackBar(
|
||||||
content: const Text('Vui lòng chọn đơn vị kinh doanh'),
|
content: Text('Vui lòng chọn đơn vị kinh doanh'),
|
||||||
backgroundColor: AppColors.danger,
|
backgroundColor: AppColors.danger,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -146,249 +182,252 @@ class _BusinessUnitSelectionPageState extends State<BusinessUnitSelectionPage> {
|
|||||||
const SizedBox(width: AppSpacing.sm),
|
const SizedBox(width: AppSpacing.sm),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: Padding(
|
||||||
child: Padding(
|
padding: const EdgeInsets.all(AppSpacing.lg),
|
||||||
padding: const EdgeInsets.all(AppSpacing.lg),
|
child: Column(
|
||||||
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
children: [
|
||||||
children: [
|
// Logo Section
|
||||||
// Logo Section
|
Center(
|
||||||
Center(
|
child: Container(
|
||||||
child: Container(
|
padding: const EdgeInsets.all(20),
|
||||||
padding: const EdgeInsets.all(20),
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
gradient: const LinearGradient(
|
||||||
gradient: const LinearGradient(
|
begin: Alignment.topLeft,
|
||||||
begin: Alignment.topLeft,
|
end: Alignment.bottomRight,
|
||||||
end: Alignment.bottomRight,
|
colors: [AppColors.primaryBlue, AppColors.lightBlue],
|
||||||
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),
|
Text(
|
||||||
),
|
'Worker App',
|
||||||
child: const Column(
|
style: TextStyle(color: Colors.white, fontSize: 12),
|
||||||
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),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
const SizedBox(height: AppSpacing.xl),
|
const SizedBox(height: AppSpacing.xl),
|
||||||
|
|
||||||
// Welcome Message
|
// Welcome Message
|
||||||
const Text(
|
const Text(
|
||||||
'Chọn đơn vị kinh doanh để tiếp tục',
|
'Chọn đơn vị kinh doanh để tiếp tục',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(color: AppColors.grey500, fontSize: 14),
|
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
|
return Container(
|
||||||
Column(
|
margin: EdgeInsets.only(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
bottom: isLast ? 0 : AppSpacing.xs,
|
||||||
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,
|
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.vertical(
|
decoration: BoxDecoration(
|
||||||
top: isFirst
|
color: AppColors.white,
|
||||||
? const Radius.circular(
|
border: Border.all(
|
||||||
InputFieldSpecs.borderRadius,
|
color: isSelected
|
||||||
)
|
? AppColors.primaryBlue
|
||||||
: Radius.zero,
|
: AppColors.grey100,
|
||||||
bottom: isLast
|
width: isSelected ? 2 : 1,
|
||||||
? 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,
|
|
||||||
),
|
),
|
||||||
child: Row(
|
borderRadius: BorderRadius.vertical(
|
||||||
children: [
|
top: isFirst
|
||||||
// Icon
|
? const Radius.circular(
|
||||||
Container(
|
InputFieldSpecs.borderRadius,
|
||||||
width: 40,
|
)
|
||||||
height: 40,
|
: Radius.zero,
|
||||||
decoration: BoxDecoration(
|
bottom: isLast
|
||||||
color: isSelected
|
? const Radius.circular(
|
||||||
? AppColors.primaryBlue.withValues(
|
InputFieldSpecs.borderRadius,
|
||||||
alpha: 0.1,
|
)
|
||||||
)
|
: Radius.zero,
|
||||||
: AppColors.grey50,
|
),
|
||||||
borderRadius: BorderRadius.circular(8),
|
boxShadow: isSelected
|
||||||
),
|
? [
|
||||||
child: Icon(
|
BoxShadow(
|
||||||
Icons.business,
|
color: AppColors.primaryBlue.withValues(
|
||||||
color: isSelected
|
alpha: 0.1,
|
||||||
? 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,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (unit.description != null) ...[
|
blurRadius: 8,
|
||||||
const SizedBox(height: 2),
|
offset: const Offset(0, 2),
|
||||||
Text(
|
),
|
||||||
unit.description!,
|
]
|
||||||
style: const TextStyle(
|
: null,
|
||||||
fontSize: 12,
|
),
|
||||||
color: AppColors.grey500,
|
child: InkWell(
|
||||||
),
|
onTap: () {
|
||||||
),
|
setState(() {
|
||||||
],
|
_selectedUnit = unit;
|
||||||
],
|
});
|
||||||
),
|
},
|
||||||
),
|
borderRadius: BorderRadius.vertical(
|
||||||
// Radio indicator
|
top: isFirst
|
||||||
Container(
|
? const Radius.circular(
|
||||||
width: 20,
|
InputFieldSpecs.borderRadius,
|
||||||
height: 20,
|
)
|
||||||
decoration: BoxDecoration(
|
: Radius.zero,
|
||||||
shape: BoxShape.circle,
|
bottom: isLast
|
||||||
border: Border.all(
|
? 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
|
color: isSelected
|
||||||
? AppColors.primaryBlue
|
? AppColors.primaryBlue
|
||||||
: AppColors.grey500,
|
: AppColors.grey500,
|
||||||
width: 2,
|
size: 20,
|
||||||
),
|
),
|
||||||
color: isSelected
|
|
||||||
? AppColors.primaryBlue
|
|
||||||
: Colors.transparent,
|
|
||||||
),
|
),
|
||||||
child: isSelected
|
const SizedBox(width: AppSpacing.md),
|
||||||
? const Icon(
|
// Unit Name
|
||||||
Icons.circle,
|
Expanded(
|
||||||
size: 10,
|
child: Column(
|
||||||
color: AppColors.white,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
)
|
children: [
|
||||||
: null,
|
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()),
|
||||||
}).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),
|
||||||
|
// 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),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter_html/flutter_html.dart';
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
import 'package:worker/core/constants/api_constants.dart';
|
||||||
import 'package:worker/core/constants/ui_constants.dart';
|
import 'package:worker/core/constants/ui_constants.dart';
|
||||||
import 'package:worker/core/theme/colors.dart';
|
import 'package:worker/core/theme/colors.dart';
|
||||||
import 'package:worker/features/news/domain/entities/news_article.dart';
|
import 'package:worker/features/news/domain/entities/news_article.dart';
|
||||||
@@ -159,76 +161,73 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
|||||||
|
|
||||||
// Article Body - Render HTML content
|
// Article Body - Render HTML content
|
||||||
if (article.content != null && article.content!.isNotEmpty)
|
if (article.content != null && article.content!.isNotEmpty)
|
||||||
Container(
|
Html(
|
||||||
// Wrap Html in Container to prevent rendering issues
|
data: article.content,
|
||||||
child: Html(
|
style: {
|
||||||
data: article.content,
|
"body": Style(
|
||||||
style: {
|
margin: Margins.zero,
|
||||||
"body": Style(
|
padding: HtmlPaddings.zero,
|
||||||
margin: Margins.zero,
|
fontSize: FontSize(16),
|
||||||
padding: HtmlPaddings.zero,
|
lineHeight: const LineHeight(1.7),
|
||||||
fontSize: FontSize(16),
|
color: const Color(0xFF1E293B),
|
||||||
lineHeight: const LineHeight(1.7),
|
),
|
||||||
color: const Color(0xFF1E293B),
|
"h2": Style(
|
||||||
|
fontSize: FontSize(20),
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: const Color(0xFF1E293B),
|
||||||
|
margin: Margins.only(top: 32, bottom: 16),
|
||||||
|
),
|
||||||
|
"h3": Style(
|
||||||
|
fontSize: FontSize(18),
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: const Color(0xFF1E293B),
|
||||||
|
margin: Margins.only(top: 24, bottom: 12),
|
||||||
|
),
|
||||||
|
"p": Style(
|
||||||
|
fontSize: FontSize(16),
|
||||||
|
color: const Color(0xFF1E293B),
|
||||||
|
lineHeight: const LineHeight(1.7),
|
||||||
|
margin: Margins.only(bottom: 16),
|
||||||
|
),
|
||||||
|
"strong": Style(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: const Color(0xFF1E293B),
|
||||||
|
),
|
||||||
|
"img": Style(
|
||||||
|
margin: Margins.symmetric(vertical: 16),
|
||||||
|
),
|
||||||
|
"ul": Style(
|
||||||
|
margin: Margins.only(left: 16, bottom: 16),
|
||||||
|
),
|
||||||
|
"ol": Style(
|
||||||
|
margin: Margins.only(left: 16, bottom: 16),
|
||||||
|
),
|
||||||
|
"li": Style(
|
||||||
|
fontSize: FontSize(16),
|
||||||
|
color: const Color(0xFF1E293B),
|
||||||
|
lineHeight: const LineHeight(1.5),
|
||||||
|
margin: Margins.only(bottom: 8),
|
||||||
|
),
|
||||||
|
"blockquote": Style(
|
||||||
|
backgroundColor: const Color(0xFFF0F9FF),
|
||||||
|
border: const Border(
|
||||||
|
left: BorderSide(color: AppColors.primaryBlue, width: 4),
|
||||||
),
|
),
|
||||||
"h2": Style(
|
padding: HtmlPaddings.all(16),
|
||||||
fontSize: FontSize(20),
|
margin: Margins.symmetric(vertical: 24),
|
||||||
fontWeight: FontWeight.w600,
|
fontStyle: FontStyle.italic,
|
||||||
color: const Color(0xFF1E293B),
|
),
|
||||||
margin: Margins.only(top: 32, bottom: 16),
|
"div": Style(
|
||||||
),
|
margin: Margins.zero,
|
||||||
"h3": Style(
|
padding: HtmlPaddings.zero,
|
||||||
fontSize: FontSize(18),
|
),
|
||||||
fontWeight: FontWeight.w600,
|
},
|
||||||
color: const Color(0xFF1E293B),
|
onLinkTap: (url, attributes, element) {
|
||||||
margin: Margins.only(top: 24, bottom: 12),
|
// Handle link taps if needed
|
||||||
),
|
if (url != null) {
|
||||||
"p": Style(
|
debugPrint('Link tapped: $url');
|
||||||
fontSize: FontSize(16),
|
}
|
||||||
color: const Color(0xFF1E293B),
|
},
|
||||||
lineHeight: const LineHeight(1.7),
|
|
||||||
margin: Margins.only(bottom: 16),
|
|
||||||
),
|
|
||||||
"strong": Style(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: const Color(0xFF1E293B),
|
|
||||||
),
|
|
||||||
"img": Style(
|
|
||||||
margin: Margins.symmetric(vertical: 16),
|
|
||||||
),
|
|
||||||
"ul": Style(
|
|
||||||
margin: Margins.only(left: 16, bottom: 16),
|
|
||||||
),
|
|
||||||
"ol": Style(
|
|
||||||
margin: Margins.only(left: 16, bottom: 16),
|
|
||||||
),
|
|
||||||
"li": Style(
|
|
||||||
fontSize: FontSize(16),
|
|
||||||
color: const Color(0xFF1E293B),
|
|
||||||
lineHeight: const LineHeight(1.5),
|
|
||||||
margin: Margins.only(bottom: 8),
|
|
||||||
),
|
|
||||||
"blockquote": Style(
|
|
||||||
backgroundColor: const Color(0xFFF0F9FF),
|
|
||||||
border: const Border(
|
|
||||||
left: BorderSide(color: AppColors.primaryBlue, width: 4),
|
|
||||||
),
|
|
||||||
padding: HtmlPaddings.all(16),
|
|
||||||
margin: Margins.symmetric(vertical: 24),
|
|
||||||
fontStyle: FontStyle.italic,
|
|
||||||
),
|
|
||||||
"div": Style(
|
|
||||||
margin: Margins.zero,
|
|
||||||
padding: HtmlPaddings.zero,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
onLinkTap: (url, attributes, element) {
|
|
||||||
// Handle link taps if needed
|
|
||||||
if (url != null) {
|
|
||||||
debugPrint('Link tapped: $url');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
@@ -256,9 +255,8 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
|||||||
|
|
||||||
/// Build metadata
|
/// Build metadata
|
||||||
Widget _buildMetadata(NewsArticle article) {
|
Widget _buildMetadata(NewsArticle article) {
|
||||||
return Wrap(
|
return Row(
|
||||||
spacing: 16,
|
spacing: 16,
|
||||||
runSpacing: 8,
|
|
||||||
children: [
|
children: [
|
||||||
// Category badge
|
// Category badge
|
||||||
Container(
|
Container(
|
||||||
@@ -281,13 +279,13 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
|||||||
_buildMetaItem(Icons.calendar_today, article.formattedDate),
|
_buildMetaItem(Icons.calendar_today, article.formattedDate),
|
||||||
|
|
||||||
// Reading time
|
// Reading time
|
||||||
_buildMetaItem(Icons.schedule, article.readingTimeText),
|
// _buildMetaItem(Icons.schedule, article.readingTimeText),
|
||||||
|
|
||||||
// Views
|
// Views
|
||||||
_buildMetaItem(
|
// _buildMetaItem(
|
||||||
Icons.visibility,
|
// Icons.visibility,
|
||||||
'${article.formattedViewCount} lượt xem',
|
// '${article.formattedViewCount} lượt xem',
|
||||||
),
|
// ),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -362,48 +360,27 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
|||||||
Widget _buildSocialActions(NewsArticle article) {
|
Widget _buildSocialActions(NewsArticle article) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
border: Border.symmetric(
|
border: Border.symmetric(
|
||||||
horizontal: BorderSide(color: const Color(0xFFE2E8F0)),
|
horizontal: BorderSide(color: Color(0xFFE2E8F0)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
// Engagement stats
|
_buildActionButton(
|
||||||
Wrap(
|
icon: _isLiked ? Icons.favorite : Icons.favorite_border,
|
||||||
spacing: 16,
|
onPressed: _onLikeTap,
|
||||||
runSpacing: 8,
|
color: _isLiked ? Colors.red : null,
|
||||||
children: [
|
|
||||||
_buildStatItem(Icons.favorite, '${article.likeCount} lượt thích'),
|
|
||||||
_buildStatItem(
|
|
||||||
Icons.comment,
|
|
||||||
'${article.commentCount} bình luận',
|
|
||||||
),
|
|
||||||
_buildStatItem(Icons.share, '${article.shareCount} lượt chia sẻ'),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
const SizedBox(height: 16),
|
_buildActionButton(
|
||||||
|
icon: _isBookmarked ? Icons.bookmark : Icons.bookmark_border,
|
||||||
// Action buttons
|
onPressed: _onBookmarkTap,
|
||||||
Row(
|
color: _isBookmarked ? AppColors.warning : null,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
_buildActionButton(
|
|
||||||
icon: _isLiked ? Icons.favorite : Icons.favorite_border,
|
|
||||||
onPressed: _onLikeTap,
|
|
||||||
color: _isLiked ? Colors.red : null,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
_buildActionButton(
|
|
||||||
icon: _isBookmarked ? Icons.bookmark : Icons.bookmark_border,
|
|
||||||
onPressed: _onBookmarkTap,
|
|
||||||
color: _isBookmarked ? AppColors.warning : null,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
_buildActionButton(icon: Icons.share, onPressed: _onShareTap),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
_buildActionButton(icon: Icons.share, onPressed: _onShareTap),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -577,21 +554,21 @@ class _NewsDetailPageState extends ConsumerState<NewsDetailPage> {
|
|||||||
/// Handle share tap
|
/// Handle share tap
|
||||||
void _onShareTap() {
|
void _onShareTap() {
|
||||||
// Copy link to clipboard
|
// Copy link to clipboard
|
||||||
Clipboard.setData(
|
// Clipboard.setData(
|
||||||
ClipboardData(text: 'https://worker.app/news/${widget.articleId}'),
|
// ClipboardData(text: 'https://worker.app/news/${widget.articleId}'),
|
||||||
);
|
// );
|
||||||
|
//
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
// ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
// const SnackBar(
|
||||||
content: Text('Đã sao chép link bài viết!'),
|
// content: Text('Đã sao chép link bài viết!'),
|
||||||
duration: Duration(seconds: 2),
|
// duration: Duration(seconds: 2),
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
|
|
||||||
// TODO: Implement native share when share_plus package is added
|
// TODO: Implement native share when share_plus package is added
|
||||||
// Share.share(
|
SharePlus.instance.share(
|
||||||
// 'Xem bài viết: ${article.title}\nhttps://worker.app/news/${article.id}',
|
ShareParams(text: 'Xem bài viết: ${ApiConstants.baseUrl}')
|
||||||
// subject: article.title,
|
);
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import 'package:intl/intl.dart';
|
|||||||
import 'package:shimmer/shimmer.dart';
|
import 'package:shimmer/shimmer.dart';
|
||||||
import 'package:worker/core/constants/ui_constants.dart';
|
import 'package:worker/core/constants/ui_constants.dart';
|
||||||
import 'package:worker/core/theme/colors.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/features/products/domain/entities/product.dart';
|
||||||
import 'package:worker/generated/l10n/app_localizations.dart';
|
import 'package:worker/generated/l10n/app_localizations.dart';
|
||||||
|
|
||||||
@@ -38,7 +37,7 @@ class ProductCard extends ConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final l10n = AppLocalizations.of(context);
|
final l10n = AppLocalizations.of(context);
|
||||||
final isFavorited = ref.watch(isFavoriteProvider(product.productId));
|
// final isFavorited = ref.watch(isFavoriteProvider(product.productId));
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
elevation: ProductCardSpecs.elevation,
|
elevation: ProductCardSpecs.elevation,
|
||||||
@@ -135,64 +134,64 @@ class ProductCard extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
|
|
||||||
// Favorite Button (bottom-left corner)
|
// Favorite Button (bottom-left corner)
|
||||||
Positioned(
|
// Positioned(
|
||||||
bottom: AppSpacing.sm,
|
// bottom: AppSpacing.sm,
|
||||||
left: AppSpacing.sm,
|
// left: AppSpacing.sm,
|
||||||
child: Material(
|
// child: Material(
|
||||||
color: Colors.transparent,
|
// color: Colors.transparent,
|
||||||
child: InkWell(
|
// child: InkWell(
|
||||||
onTap: () async {
|
// onTap: () async {
|
||||||
// Capture current state before toggle
|
// // Capture current state before toggle
|
||||||
final wasfavorited = isFavorited;
|
// final wasfavorited = isFavorited;
|
||||||
|
//
|
||||||
// Toggle favorite
|
// // Toggle favorite
|
||||||
await ref
|
// await ref
|
||||||
.read(favoritesProvider.notifier)
|
// .read(favoritesProvider.notifier)
|
||||||
.toggleFavorite(product.productId);
|
// .toggleFavorite(product.productId);
|
||||||
|
//
|
||||||
// Show feedback with correct message
|
// // Show feedback with correct message
|
||||||
if (context.mounted) {
|
// if (context.mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
// ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
// SnackBar(
|
||||||
content: Text(
|
// content: Text(
|
||||||
wasfavorited
|
// wasfavorited
|
||||||
? 'Đã xóa khỏi yêu thích'
|
// ? 'Đã xóa khỏi yêu thích'
|
||||||
: 'Đã thêm vào yêu thích',
|
// : 'Đã thêm vào yêu thích',
|
||||||
),
|
// ),
|
||||||
duration: const Duration(seconds: 1),
|
// duration: const Duration(seconds: 1),
|
||||||
behavior: SnackBarBehavior.floating,
|
// behavior: SnackBarBehavior.floating,
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
borderRadius: BorderRadius.circular(20),
|
// borderRadius: BorderRadius.circular(20),
|
||||||
child: Container(
|
// child: Container(
|
||||||
width: 36,
|
// width: 36,
|
||||||
height: 36,
|
// height: 36,
|
||||||
decoration: BoxDecoration(
|
// decoration: BoxDecoration(
|
||||||
color: AppColors.white,
|
// color: AppColors.white,
|
||||||
shape: BoxShape.circle,
|
// shape: BoxShape.circle,
|
||||||
boxShadow: [
|
// boxShadow: [
|
||||||
BoxShadow(
|
// BoxShadow(
|
||||||
color: Colors.black.withValues(alpha: 0.15),
|
// color: Colors.black.withValues(alpha: 0.15),
|
||||||
blurRadius: 6,
|
// blurRadius: 6,
|
||||||
offset: const Offset(0, 2),
|
// offset: const Offset(0, 2),
|
||||||
),
|
// ),
|
||||||
],
|
// ],
|
||||||
),
|
// ),
|
||||||
child: Icon(
|
// child: Icon(
|
||||||
isFavorited
|
// isFavorited
|
||||||
? Icons.favorite
|
// ? Icons.favorite
|
||||||
: Icons.favorite_border,
|
// : Icons.favorite_border,
|
||||||
color: isFavorited
|
// color: isFavorited
|
||||||
? AppColors.danger
|
// ? AppColors.danger
|
||||||
: AppColors.grey500,
|
// : AppColors.grey500,
|
||||||
size: 20,
|
// size: 20,
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
16
pubspec.lock
16
pubspec.lock
@@ -413,10 +413,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: file_picker
|
name: file_picker
|
||||||
sha256: "825aec673606875c33cd8d3c4083f1a3c3999015a84178b317b7ef396b7384f3"
|
sha256: ab13ae8ef5580a411c458d6207b6774a6c237d77ac37011b13994879f68a8810
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.0.7"
|
version: "8.3.7"
|
||||||
file_selector_linux:
|
file_selector_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1160,18 +1160,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: share_plus
|
name: share_plus
|
||||||
sha256: ef3489a969683c4f3d0239010cc8b7a2a46543a8d139e111c06c558875083544
|
sha256: "14c8860d4de93d3a7e53af51bff479598c4e999605290756bbbe45cf65b37840"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.0.0"
|
version: "12.0.1"
|
||||||
share_plus_platform_interface:
|
share_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: share_plus_platform_interface
|
name: share_plus_platform_interface
|
||||||
sha256: "0f9e4418835d1b2c3ae78fdb918251959106cefdbc4dd43526e182f80e82f6d4"
|
sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.0"
|
version: "6.1.0"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1565,10 +1565,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web
|
name: web
|
||||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.1"
|
version: "1.1.1"
|
||||||
web_socket:
|
web_socket:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.0.0+1
|
version: 1.0.0+3
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.9.2
|
sdk: ^3.9.2
|
||||||
@@ -63,9 +63,10 @@ dependencies:
|
|||||||
qr_flutter: ^4.1.0
|
qr_flutter: ^4.1.0
|
||||||
mobile_scanner: ^5.2.3
|
mobile_scanner: ^5.2.3
|
||||||
|
|
||||||
|
|
||||||
# Utilities
|
# Utilities
|
||||||
intl: ^0.20.0
|
intl: ^0.20.0
|
||||||
share_plus: ^9.0.0
|
share_plus: ^12.0.1
|
||||||
image_picker: ^1.1.2
|
image_picker: ^1.1.2
|
||||||
file_picker: ^8.0.0
|
file_picker: ^8.0.0
|
||||||
url_launcher: ^6.3.0
|
url_launcher: ^6.3.0
|
||||||
|
|||||||
Reference in New Issue
Block a user