Server : Apache/2.4.18 (Ubuntu) System : Linux canvaswebdesign 3.13.0-71-generic #114-Ubuntu SMP Tue Dec 1 02:34:22 UTC 2015 x86_64 User : oppastar ( 1041) PHP Version : 7.0.33-0ubuntu0.16.04.15 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority, Directory : /proc/self/root/var/www/laciasmara.com/public_html/shop/application/views/themes/3/ |
Upload File : |
<style> .product-container { margin: 0 auto; padding: 48px 60px; color: #333; } .product-section-grid { display: grid; grid-template-columns: 3fr 5fr 3fr; gap: 24px; } .purchase-section { background: #fff; box-sizing: border-box; border-radius: 8px; top: 20px; height: fit-content; } .current-product-price { font-size: 24px; margin-bottom: 8px; color: #333; } .original-product-price { font-size: 18px; color: #999; text-decoration: line-through; margin-left: 6px; } .action-buttons { display: flex; flex-direction: column; gap: 10px; } /* Image Gallery */ .image-gallery { position: relative; max-width: 400px; width: 100%; box-sizing: border-box; } .main-image { width: 400px; height: 400px; object-fit: contain; border-radius: 8px; margin-bottom: 10px; } .thumbnail-container { display: flex; gap: 8px; max-width: 400px; width: 100%; margin-top: 16px; overflow-x: auto; overflow-y: hidden; padding: 4px 0; box-sizing: border-box; scrollbar-width: thin; scrollbar-color: #ccc #f1f1f1; -webkit-overflow-scrolling: touch; } .thumbnail-container::-webkit-scrollbar { height: 6px; } .thumbnail-container::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 3px; } .thumbnail-container::-webkit-scrollbar-thumb { background: #ccc; border-radius: 3px; } .thumbnail-container::-webkit-scrollbar-thumb:hover { background: #999; } .thumbnail-image-container { flex-shrink: 0; position: relative; /* Pastikan ukuran minimum */ min-width: 72px; width: 72px; } .thumbnail { width: 72px; height: 72px; object-fit: contain; border-radius: 4px; cursor: pointer; opacity: 0.7; transition: opacity 0.3s; position: relative; } .thumbnail:hover { opacity: 1; } .thumbnail.active { opacity: 1; border: 2px solid #7a4397; } @media (max-width: 768px) { .image-gallery { max-width: 100%; width: 100%; margin: 0; padding: 0; /* Tambahkan padding horizontal untuk breathing room */ box-sizing: border-box; overflow: hidden; } .main-image { width: 100%; max-width: calc(100vw - 32px); height: 350px; box-sizing: border-box; } .thumbnail-container { /* KUNCI: Batasi container dengan ketat */ max-width: calc(100vw - 32px); /* Sesuaikan dengan padding image-gallery */ width: calc(100vw - 32px); overflow-x: auto; overflow-y: hidden; gap: 6px; padding: 4px 0 8px 0; -webkit-overflow-scrolling: touch; margin: 12px 0 0 0; box-sizing: border-box; /* Tambahan untuk memaksa container tidak melebar */ min-width: 0; /* Penting untuk flexbox */ flex-shrink: 1; } .thumbnail-image-container { /* Pastikan thumbnail tidak menyusut di mobile */ flex-shrink: 0; min-width: 60px; width: 60px; } .thumbnail { width: 60px; height: 60px; /* Pastikan ukuran tetap */ flex-shrink: 0; } /* Scrollbar mobile yang lebih tebal agar mudah digunakan */ .thumbnail-container::-webkit-scrollbar { height: 8px; } .thumbnail-container::-webkit-scrollbar-thumb { background: #999; border-radius: 4px; } } .main-image-container { position: relative; width: 100%; overflow: hidden; border: 1px solid #ddd; cursor: zoom-in; box-sizing: border-box; } .main-image-container:hover img { transform: scale(1.5); } .main-image-container img.zoom { transform-origin: center center; transition: none; } .main-image-container:not(.thumbnail-container .main-image-container) .out-of-stock-text { font-size: 1em; } /* Product Info */ .product-title-main { font-size: 24px; font-weight: bold; height: max-content; margin-top: 0; margin-bottom: 20px; } .rating { display: flex; align-items: center; gap: 10px; margin-bottom: 20px; color: #666; } .price { font-weight: bold; } /* Details */ .details { margin-bottom: 20px; } .detail-row { display: flex; margin-bottom: 10px; } .detail-label { width: 150px; color: #666; } /* Quantity Selector */ .quantity-selector { width: 100%; display: flex; align-items: center; gap: 10px; margin-bottom: 20px; justify-content: space-between; } .qty-btn { width: 45px; height: 45px; border: 1px solid #ddd; border-radius: 8px; background: #f3f4f6; color: #333; font-size: 18px; font-weight: bold; cursor: pointer; transition: all 0.3s; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .qty-btn:hover { background: #e5e7eb; border-color: #cbd5e0; } .qty-btn:active { background: #d1d5db; } .qty-input { width: 80px; text-align: center; border: 1px solid #ddd; border-radius: 8px; padding: 10px; font-size: 16px; font-weight: bold; color: #333; background: #fff; box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); transition: border-color 0.3s; } .qty-input:focus { outline: none; border-color: #7a4397; } .quantity-container { display: flex; align-items: center; margin-top: 20px; } /* Action Buttons */ .button-cart { width: 100%; padding: 12px; font-size: 16px; border: none; border-radius: 8px; font-weight: 600; cursor: pointer; transition: all 0.3s; margin-bottom: 10px; background: #7A4397; color: white; } .button-cart:hover { background: #653680; } /* Social Actions */ .social-actions { display: flex; gap: 20px; margin-top: 20px; } .social-btn { display: flex; align-items: center; gap: 5px; color: #666; border: none; background: none; cursor: pointer; transition: color 0.3s; } .social-btn:hover { color: #333; } .tabs { margin-top: 20px; border-bottom: 1px solid #ddd; } .tab-buttons { display: flex; gap: 20px; overflow-x: auto; padding-bottom: 10px; } .tab-btn { border: none; background: none; padding: 10px 5px; cursor: pointer; color: #666; position: relative; font-size: 16px; white-space: nowrap; } .tab-btn.active { color: #7A4397; font-weight: 600; } .tab-btn.active::after { content: ''; position: absolute; bottom: -10px; left: 0; width: 100%; height: 2px; background: #7A4397; } .tab-content { display: none; padding: 12px 0; line-height: 1.6; } .tab-content a { color: #7A4397; text-decoration: none; } .tab-content.active { display: block; } .purchase-info { margin-top: 20px; display: flex; flex-direction: column; gap: 10px; } .info-item { position: relative; display: flex; align-items: center; gap: 10px; padding: 10px; background-color: #f9f9f9; border: 1px solid #ccc; border-radius: 5px; cursor: pointer; transition: background-color 0.3s, border-color 0.3s; } .info-item:hover { background-color: #e0e0e0; border-color: #888; } .info-icon { width: 32px; height: 32px; object-fit: contain; flex-shrink: 0; } .info-item span { flex: 1; font-size: 14px; color: #333; line-height: 1.5; } .variant-options { display: flex; flex-wrap: wrap; gap: 10px; margin: 16px 0; } .variant { border: 1px solid #ccc; cursor: pointer; border-radius: 5px; transition: all 0.3s ease; } .variant-section { margin-top: 20px; } .variant-section h4 { margin-bottom: 0; } .color-variant { width: 30px; height: 30px; display: inline-block; border-radius: 50%; border: 2px solid #ccc; cursor: pointer; transition: all 0.3s ease; } .color-variant:hover { border-color: #888; transform: scale(1.05); } .color-variant.active { border-color: #7A4397; transform: scale(1.05); } .text-variant { background-color: #f2f2f2; padding: 8px 12px; } .variant:hover { background-color: #e0e0e0; border-color: #888; } .variant[title]:hover::after { content: attr(title); position: absolute; background-color: rgba(0, 0, 0, 0.75); color: white; padding: 5px 10px; border-radius: 5px; font-size: 14px; top: -35px; left: 50%; transform: translateX(-50%); white-space: nowrap; z-index: 10; } /* Styling untuk varian yang aktif (misalnya yang dipilih) */ .variant.active { border-color: #7A4397; background-color: rgb(233, 198, 255); } .fab-cart { display: none; } @media (max-width: 768px) { .original-product-price { font-size: 16px; margin-left: 2px; } .product-container { padding: 20px; } .product-section-grid { grid-template-columns: 1fr; } .quantity-container { margin-top: 0px; } .product-title-main { font-size: 20px; } .price { text-align: center; } .qty-btn { width: 35px; height: 35px; font-size: 18px; } .qty-input { width: 70px; font-size: 16px; } .tab-content { font-size: 16px; } .rating { font-size: 14px; } .price { font-size: 24px; } .tabs .tab-buttons { gap: 10px; } .tab-content { padding: 10px 0; } .variant-options { gap: 8px; } .variant-section { margin-top: 10px; } /* New sticky cart styles */ .fab-cart { position: fixed; bottom: -60px; right: 20px; z-index: 1000; width: 50px; height: 50px; border-radius: 50%; background: #7A4397; color: white; border: none; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: bottom 0.5s ease-out, transform 0.3s ease-in-out; opacity: 0; /* Awalnya tidak terlihat */ } /* Saat tombol muncul */ .fab-cart.show { bottom: 85px; opacity: 1; } .fab-cart:active { transform: scale(0.92); } /* Tooltip kecil untuk memberikan informasi tambahan */ .fab-cart::after { content: "Add to Cart"; position: absolute; bottom: 12px; right: 55px; background: rgba(0, 0, 0, 0.75); color: white; padding: 6px 10px; border-radius: 5px; font-size: 12px; white-space: nowrap; opacity: 0; visibility: hidden; transition: opacity 0.3s ease-in-out; } .fab-cart:hover::after { opacity: 1; visibility: visible; } .original-price { font-size: 12px; } } .overlay-stock { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.7); display: flex; align-items: center; justify-content: center; border-radius: 4px; } .out-of-stock-text { color: white; font-size: 0.7em; font-weight: bold; text-align: center; } .popup-image-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.8); display: flex; align-items: center; justify-content: center; z-index: 9999; opacity: 0; visibility: hidden; transition: opacity 0.3s ease, visibility 0.3s ease; } .popup-image-overlay.active { opacity: 1; visibility: visible; } .popup-image-overlay .popup-image { max-width: 90%; max-height: 90%; border-radius: 5px; background-color: white; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); cursor: zoom-out; } .popup-image-overlay .arrow { position: absolute; top: 50%; transform: translateY(-50%); background-color: rgba(255, 255, 255, 0.7); border: none; border-radius: 50%; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 20px; font-weight: bold; } .popup-image-overlay .left-arrow { left: 400px; } .popup-image-overlay .right-arrow { right: 400px; } @media (hover: none) and (pointer: coarse) { .main-image { transform: none !important; transition: none !important; } .main-image-container:hover .main-image { transform: none !important; } } .thumbnail-container .main-image-container { width: auto; } /* Review */ .reviews-section { margin: 36px 0; } .review-summary { background-color: #fff; /* padding: 1.5rem; */ border-radius: 12px; /* box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); */ /* border: 0.25px solid #e5e7eb; */ } .review-title { font-size: 1.5rem; font-weight: 700; margin: 0 0 1.5rem 0; color: #111827; } .review-content { display: grid; grid-template-columns: 1fr 2fr; gap: 2rem; align-items: start; } /* Left Side - Average Rating */ .rating-overview { text-align: center; padding: 1.5rem; background: white; border-radius: 12px; border: 1px solid #7A4397; transition: all 0.3s ease; } .rating-overview:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(122, 67, 151, 0.15); } .average-rating { font-size: 3.5rem; font-weight: 700; color: #7A4397; line-height: 1; margin-bottom: 0.5rem; } .rating-stars { display: flex; justify-content: center; align-items: center; gap: 0.25rem; margin-bottom: 0.75rem; } .product-rating-stars { display: flex; /* justify-content: center; */ align-items: center; gap: 0.25rem; margin-bottom: 0.75rem; } .star { width: 1.5rem; height: 1.5rem; transition: all 0.2s ease; } .star.filled { color: #7A4397; filter: drop-shadow(0 1px 2px rgba(122, 67, 151, 0.3)); } .star.empty { color: #d1d5db; } .total-reviews { font-size: 1rem; font-weight: 500; color: #7A4397; } .reviews-text { font-size: 0.875rem; color: #7A4397; margin-top: 0.25rem; } /* Right Side - Rating Breakdown */ .rating-breakdown { display: flex; flex-direction: column; gap: 0.5rem; } .breakdown-title { font-size: 1rem; font-weight: 600; color: #333; margin-bottom: 0.5rem; } .rating-row { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0; } .rating-row:hover .progress-bar { box-shadow: 0 0 0 2px rgba(251, 191, 36, 0.2); } .rating-label { display: flex; align-items: center; gap: 0.5rem; min-width: 50px; font-size: 14px; font-weight: 500; color: #333; } .rating-label .star { width: 1rem; height: 1rem; color: #7A4397; } .progress-container { flex: 1; background-color: #f3f4f6; border-radius: 6px; height: 8px; overflow: hidden; position: relative; } .progress-bar { height: 100%; /* background: linear-gradient(90deg, #fbbf24 0%, #f59e0b 100%); */ background: linear-gradient(90deg, #7A4397 0%, #B57EDC 100%); border-radius: 6px; transition: width 0.8s ease-out; position: relative; } .progress-bar::after { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%); animation: shimmer 2s infinite; } @keyframes shimmer { 0% { transform: translateX(-100%); } 100% { transform: translateX(100%); } } .rating-count { min-width: 40px; text-align: right; font-size: 0.875rem; font-weight: 500; color: #6b7280; } /* No Reviews State */ .no-reviews { text-align: center; padding: 3rem 2rem; color: #9ca3af; background: #f9fafb; border-radius: 12px; border: 2px dashed #d1d5db; } .no-reviews-icon { width: 4rem; height: 4rem; margin: 0 auto 1rem; color: #d1d5db; } .no-reviews-title { font-size: 1.25rem; font-weight: 600; margin-bottom: 0.5rem; color: #6b7280; } .no-reviews-text { font-size: 1rem; color: #9ca3af; } /* Responsive Design */ @media (max-width: 768px) { .review-content { grid-template-columns: 1fr; gap: 2rem; } .rating-overview { text-align: center; } .average-rating { font-size: 2.5rem; } .rating-row { gap: 0.5rem; } .rating-label { min-width: 60px; } } .review-list { display: flex; flex-direction: column; gap: 1.5rem; padding: 0; max-width: 100%; } /* Individual Review Item */ .review-item { background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 12px; padding: 1.5rem; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); transition: all 0.3s ease; position: relative; overflow: hidden; } .review-item:hover { box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); transform: translateY(-2px); border-color: #d1d5db; } /* Review Header */ .review-item-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 1rem; flex-wrap: wrap; gap: 0.75rem; } /* Reviewer Info */ .reviewer-info { display: flex; align-items: center; gap: 0.75rem; } .reviewer-avatar { width: 40px; height: 40px; border-radius: 50%; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); display: flex; align-items: center; justify-content: center; color: white; font-weight: 600; font-size: 16px; flex-shrink: 0; } .reviewer-details { display: flex; flex-direction: column; gap: 0.25rem; } .reviewer-name { font-weight: 600; font-size: 1rem; color: #1f2937; margin: 0; } .reviewer-name span { font-size: 0.75rem; font-weight: 400; color: #6b7280; text-transform: uppercase; letter-spacing: 0.05em; } /* Review Meta Information */ .review-meta { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; } /* Rating Stars */ .review-rating-stars { display: flex; align-items: center; gap: 0.125rem; } .rating-value { font-size: 0.875rem; font-weight: 600; color: #374151; margin-left: 0.25rem; } /* Review Date */ .review-date { font-size: 0.875rem; color: #6b7280; font-weight: 500; background-color: #f3f4f6; padding: 0.25rem 0.5rem; border-radius: 6px; } /* Review Subject */ .review-subject { font-size: 1.125rem; font-weight: 600; color: #1f2937; margin: 0 0 0.75rem 0; line-height: 1.4; } .review-filter-container { background: #ffffff; border: 1px solid #e5e7eb; border-radius: 12px; padding: 20px; margin-bottom: 2rem; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); } .filter-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem; flex-wrap: wrap; gap: 1rem; } .filter-title { font-size: 1.125rem; font-weight: 600; color: #1f2937; margin: 0; display: flex; align-items: center; gap: 0.5rem; } .filter-icon { width: 20px; height: 20px; color: #6b7280; } .filter-reset { background: none; border: 1px solid #d1d5db; color: #6b7280; padding: 0.5rem 1rem; border-radius: 8px; font-size: 0.875rem; cursor: pointer; transition: all 0.2s ease; } .filter-reset:hover { background-color: #f3f4f6; border-color: #9ca3af; } .filter-options { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; } .filter-group { display: flex; flex-direction: column; gap: 0.5rem; } .filter-label { font-size: 0.875rem; font-weight: 500; color: #374151; margin-bottom: 0.5rem; } .filter-select { padding: 0.75rem; border: 1px solid #d1d5db; border-radius: 8px; font-size: 0.875rem; background-color: #ffffff; color: #374151; cursor: pointer; transition: all 0.2s ease; } .filter-select:focus { outline: none; border-color: #667eea; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); } .filter-select:hover { border-color: #9ca3af; } .star-filter-buttons { display: flex; gap: 0.5rem; flex-wrap: wrap; } .star-filter-btn { display: flex; align-items: center; gap: 0.25rem; padding: 0.5rem 0.75rem; border: 1px solid #d1d5db; background-color: #ffffff; border-radius: 8px; font-size: 0.875rem; cursor: pointer; transition: all 0.2s ease; } .star-filter-btn:hover { background-color: #f3f4f6; border-color: #9ca3af; } .star-filter-btn.active { background-color: #7A4397; border-color: #7A4397; color: #ffffff; transition: all 0.3s ease; } .star-filter-btn.active .star { color: #ffffff; } .star-filter-btn .star { width: 14px; height: 14px; } .results-info { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; padding: 0.75rem 1rem; background-color: #f8fafc; border-radius: 8px; font-size: 0.875rem; color: #64748b; } .results-count { font-weight: 500; } /* Animation for filtered items */ .review-item.filtered-out { display: none; } .review-item.filtered-in { animation: fadeInUp 0.4s ease-out; } @keyframes fadeInUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .no-results { text-align: center; padding: 3rem 1.5rem; color: #6b7280; } .no-results-icon { width: 48px; height: 48px; margin: 0 auto 1rem; color: #d1d5db; } /* Responsive */ @media (max-width: 768px) { .filter-options { grid-template-columns: 1fr; } .filter-header { flex-direction: column; align-items: flex-start; } .star-filter-buttons { justify-content: center; } .results-info { flex-direction: column; gap: 0.5rem; text-align: center; } } /* Review Text */ .review-text { font-size: 0.95rem; color: #4b5563; line-height: 1.6; margin: 0; text-align: justify; } /* Verified Badge */ .verified-badge { display: inline-flex; align-items: center; gap: 0.25rem; background-color: #ecfdf5; color: #065f46; font-size: 0.75rem; font-weight: 500; padding: 0.25rem 0.5rem; border-radius: 6px; } .verified-icon { width: 12px; height: 12px; } /* Empty State */ .no-reviews { text-align: center; padding: 3rem 1.5rem; color: #6b7280; } .no-reviews-icon { width: 48px; height: 48px; margin: 0 auto 1rem; color: #d1d5db; } .no-reviews h3 { font-size: 1.125rem; font-weight: 600; color: #374151; margin: 0 0 0.5rem 0; } /* Responsive Design */ @media (max-width: 768px) { .review-list { padding: 0; gap: 1rem; } .review-item { padding: 1rem; } .review-item-header { flex-direction: column; align-items: flex-start; gap: 0.75rem; } .review-meta { width: 100%; justify-content: space-between; } .reviewer-name { font-size: 0.95rem; } .review-subject { font-size: 1rem; } .review-text { font-size: 0.8rem; } } /* Animation for new reviews */ @keyframes slideInUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .review-item { animation: slideInUp 0.5s ease-out; } /* Loading state */ .review-loading { display: flex; justify-content: center; align-items: center; padding: 2rem; } .review-loading-spinner { width: 32px; height: 32px; border: 3px solid #e5e7eb; border-top: 3px solid #667eea; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .add-review-button { width: 100%; display: inline-block; background-color: #7A4397; color: white; padding: 0.75rem 1.2rem; border: none; border-radius: 0.5rem; font-size: 14px; font-weight: 500; cursor: pointer; margin: 1rem 0; transition: background-color 0.3s ease, transform 0.2s ease; } .add-review-button:hover { background-color: #69397f; /* sedikit lebih gelap */ transform: translateY(-1px); box-shadow: 0 4px 6px rgba(122, 67, 151, 0.3); } .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); justify-content: center; align-items: center; z-index: 1000; } .modal-content { background-color: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); text-align: center; max-width: 400px; width: 90%; } .close-button { position: absolute; top: 10px; right: 20px; font-size: 20px; cursor: pointer; color: #333; } .modal-image { width: 150px; height: 150px; object-fit: contain; } .hidden { display: none; } @media (max-width: 768px) { .modal-content { max-width: 70%; } } .add-review-container { border-radius: 10px; width: 100%; margin: 0; box-sizing: border-box; } .add-review-title { font-size: 1.25rem; font-weight: 600; margin-bottom: 20px; text-align: left; color: #333; } .input-group { margin-bottom: 20px; } .input-label { font-size: 1rem; font-weight: 500; color: #4a5568; display: block; margin-bottom: 8px; } .input-field { padding: 14px; border: 1px solid #cbd5e0; border-radius: 8px; font-size: 0.95rem; color: #4a5568; background-color: #fff; width: 100%; /* Ensuring all fields are full width */ box-sizing: border-box; /* Ensures padding doesn't affect width */ transition: border-color 0.2s ease; } .input-field:focus { border-color: #653680; outline: none; } .add-submit-container { margin-top: 20px; } .add-submit-button { background-color: #7A4397; color: white; padding: 14px 28px; border-radius: 8px; font-size: 1rem; border: none; cursor: pointer; width: 100%; transition: background-color 0.2s ease; } .add-submit-button:hover { background-color: #653680; } @media (max-width: 768px) { .add-submit-button { font-size: 1rem; padding: 12px 24px; } .add-to-cart-button { /* font-size: 14px; */ display: none; transform: translateY(0); } .add-to-cart-button i { font-size: 14px; } } .banner-container { display: none; } .product-suggestion-container { display: flex; width: 100%; } .product-suggestion-header { color: #333; font-size: 1.25rem; font-weight: 600; margin: 36px 0; } .product-link { text-decoration: none; color: inherit; display: block; } .product-link:hover .product-title { text-decoration: none; } .product-long-description { margin-top: 48px; } .long-description a { color: #7A4397; text-decoration: none; } @media (max-width: 768px) { .product-long-description { margin-top: 0; } .product-long-description-header { margin-top: 0; font-size: 20px; line-height: 1.6; } .long-description { font-size: 16px; line-height: 1.6; } } .reseller-media-link { display: flex; /* justify-content: center; */ margin-top: 20px; } .reseller-media-link-button { display: flex; align-items: center; justify-content: center; background-color: #7A4397; color: white; padding: 12px 20px; border-radius: 8px; font-size: 14px; font-weight: 600; text-decoration: none; gap: 10px; transition: background-color 0.3s ease, transform 0.3s ease; cursor: pointer; } .reseller-media-link-button .icon { font-size: 16px; } .reseller-media-link-button:hover { background-color: #8D4EAF; transform: scale(1.05); } .reseller-media-link-button:active { background-color: #683880; } .reseller-media-link-button:focus { outline: none; } .video-container { display: flex; justify-content: space-between; gap: 20px; /* Menambahkan jarak antar video */ margin-top: 30px; } .product-video, .product-guide { width: 48%; /* Mengatur lebar masing-masing video menjadi hampir 50% */ } .video-header { font-size: 1.5rem; font-weight: bold; color: #333; margin-bottom: 10px; } .video-container-box { position: relative; width: 100%; overflow: hidden; padding-top: 56.25%; /* Aspect ratio for 16:9 */ background-color: #f8f8f8; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); } .video-container-box iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border-radius: 8px; } @media (max-width: 768px) { .video-container { flex-direction: column; /* Menjadikan video vertikal di perangkat mobile */ } .product-video, .product-guide { width: 100%; /* Menjadikan masing-masing video memiliki lebar penuh di perangkat mobile */ margin-bottom: 20px; } .video-container-box iframe { height: 200px; /* Menyesuaikan ukuran video di versi mobile */ } } .breadcrumb-container { margin-bottom: 24px; padding: 0; background-color: #fff; max-width: 100%; overflow: hidden; } .breadcrumb-list { display: flex; align-items: center; list-style: none; padding: 0; margin: 0; flex-wrap: wrap; gap: 2px; } .breadcrumb-item { display: inline-flex; align-items: center; font-size: 14px; color: #666; overflow: hidden; max-width: 250px; } /* Remove the default ">" styling */ .breadcrumb-item:not(:first-child)::before { content: "›"; margin: 0 6px; color: #666; font-size: 16px; } /* Style for links */ .breadcrumb-link { color: #333; text-decoration: none; transition: color 0.2s ease; } .breadcrumb-link:hover { color: #7A4397; text-decoration: underline; } /* Current page styling */ .breadcrumb-current { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; color: #666; font-weight: 400; } /* Remove default list styling */ .breadcrumb-list { list-style-type: none; } /* Fix spacing issues */ .breadcrumb-separator { display: none; } /* Responsive Design */ @media (max-width: 640px) { .breadcrumb-container { margin-bottom: 12px; } .breadcrumb-item { font-size: 12px; } .breadcrumb-separator { margin: 0 6px; } } /* Optional: Animation on hover */ .breadcrumb-link { position: relative; } .breadcrumb-link::after { content: ''; position: absolute; width: 100%; height: 1px; bottom: 0; left: 0; background-color: #7A4397; transform: scaleX(0); transform-origin: bottom right; transition: transform 0.3s ease; } .breadcrumb-link:hover::after { transform: scaleX(1); transform-origin: bottom left; } /* Accessibility */ @media (prefers-reduced-motion: reduce) { .breadcrumb-link, .breadcrumb-link::after { transition: none; } } /* Style for Share and Wishlist Section */ .share-wishlist-section { display: flex; justify-content: start; align-items: center; gap: 20px; margin-top: 20px; font-size: 16px; } /* Share */ .share-icon button, .wishlist-icon button { display: flex; align-items: center; background-color: #FFFFFF; border: none; color: #333; border-radius: 30px; cursor: pointer; font-size: 16px; transition: background-color 0.3s, transform 0.2s; gap: 8px; } /* Icon inside buttons */ .share-icon svg, .wishlist-icon i { width: 24px; height: 24px; font-size: 24px; } .wishlist-icon button i { transition: color 0.3s, transform 0.2s; } /* Hover effect for buttons */ .share-icon button:hover { color: #7A4397; transform: scale(1.05); } .wishlist-icon button:hover i { color: #ff0000; } /* Responsive design */ @media (max-width: 768px) { .share-wishlist-section { gap: 8px; } .share-icon button, .wishlist-icon button { width: 100%; text-align: left; font-size: 14px; justify-content: flex-start; } /* Ukuran ikon lebih kecil untuk mobile */ .share-icon svg, .wishlist-icon i { width: 20px; height: 20px; font-size: 20px; } } .fab-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.6); z-index: 9999; display: flex; justify-content: flex-end; align-items: flex-end; opacity: 0; visibility: hidden; transition: opacity 0.3s ease-in-out, visibility 0.3s; } .fab-overlay.show { opacity: 1; visibility: visible; } .fab-tooltip { position: relative; background: white; color: #333; padding: 12px 16px; border-radius: 8px; font-size: 14px; font-weight: bold; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); text-align: center; margin-bottom: 80px; margin-right: 85px; } .fab-tooltip::after { content: ""; position: absolute; top: 50%; right: -10px; z-index: 1; /* Geser ke kanan */ transform: translateY(-50%); border-left: 10px solid white; /* Panah menunjuk ke kanan */ border-top: 7px solid transparent; border-bottom: 7px solid transparent; } .fab-tooltip p { margin: 0; } .fab-tutorial-btn { width: max-content; background: #7A4397; color: white; border: none; padding: 8px 15px; border-radius: 5px; font-size: 14px; cursor: pointer; transition: background 0.3s; position: absolute; bottom: 40px; /* Memindahkan tombol ke bawah tooltip */ left: 65%; transform: translateX(-50%); } .fab-tutorial-btn:hover { background: #682f87; } .promo-container { width: 100%; /* max-width terkomentari sesuai dengan kode Anda */ /* max-width: 650px; */ margin: 0 auto; /* border-radius terkomentari sesuai dengan kode Anda */ /* border-radius: 12px; */ overflow: hidden; box-shadow: 0 8px 20px rgba(122, 67, 151, 0.3); } /* Header banner dengan efek pola */ .promo-header { background: linear-gradient(135deg, #7A4397 30%, #C66FC0 70%); background-size: cover; background-position: center; padding: 30px 20px; text-align: center; position: relative; color: #fff; z-index: 2; } /* Efek noise halus lebih seimbang */ .promo-header::after { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-image: url('https://www.transparenttextures.com/patterns/noisy-net.png'); opacity: 1; /* Kurangi sedikit opacity agar tidak terlalu kasar */ pointer-events: none; mix-blend-mode: overlay; z-index: 1; } .promo-header h1, .promo-header p { position: relative; z-index: 3; } /* Badge flash sale */ .flash-badge { position: absolute; top: 150px; right: 10px; background-color: #FF3E3E; color: white; font-size: 0.8rem; font-weight: bold; padding: 6px 12px; border-radius: 50px; transform: rotate(15deg); box-shadow: 0 3px 6px rgba(0, 0, 0, 0.25); text-transform: uppercase; animation: pulse-badge 1.5s infinite; z-index: 9990; } @keyframes pulse-badge { 0% { transform: rotate(15deg) scale(1); } 50% { transform: rotate(15deg) scale(1.1); } 100% { transform: rotate(15deg) scale(1); } } .promo-title { color: white; font-size: 1.8rem; font-weight: 800; margin-bottom: 3px; text-transform: uppercase; letter-spacing: 1px; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); } .promo-subtitle { color: rgba(255, 255, 255, 0.9); font-size: 0.9rem; margin-bottom: 10px; } /* Styling countdown section */ .countdown-section { background: radial-gradient(circle at center, rgb(152, 115, 174), #7A4397); padding: 16px; position: relative; overflow: hidden; box-shadow: 0px 6px 15px rgba(0, 0, 0, 0.2); } /* Efek glow lebih natural */ .countdown-section::after { content: ""; position: absolute; top: 50%; left: 50%; width: 100%; height: 100%; transform: translate(-50%, -50%); filter: blur(15px); /* Tambahkan blur agar glow lebih soft */ opacity: 0.4; /* Kurangi opacity supaya tidak overexposed */ } .countdown-label { color: rgba(255, 255, 255, 0.9); font-size: 1.1rem; text-align: center; margin-bottom: 10px; position: relative; z-index: 2; } .countdown-timer { display: flex; justify-content: center; gap: 10px; position: relative; z-index: 2; } .countdown-box { display: flex; flex-direction: column; align-items: center; min-width: 55px; background-color: rgba(255, 255, 255, 0.2); /* Sedikit lebih transparan */ backdrop-filter: blur(5px); padding: 10px 8px; border-radius: 8px; box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1), inset 0 1px 1px rgba(255, 255, 255, 0.25); border: 1px solid rgba(255, 255, 255, 0.25); } .countdown-value { font-size: 1.8rem; font-weight: bold; color: white; text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3); } .countdown-unit { font-size: 0.7rem; color: rgba(255, 255, 255, 0.8); text-transform: uppercase; letter-spacing: 1px; margin-top: 3px; } .countdown-separator { display: flex; align-items: center; color: white; font-size: 1.7rem; font-weight: bold; margin-top: -5px; } .promo-description { color: #444; font-size: 0.9rem; margin-bottom: 15px; max-width: 400px; margin-left: auto; margin-right: auto; } /* Responsive styling */ @media (max-width: 768px) { .promo-title { font-size: 1.5rem; } .countdown-box { min-width: 60px; padding: 8px 5px; } .countdown-value { font-size: 1.6rem; } .flash-badge { font-size: 0.8rem; padding: 6px 12px; top: 100px; /* Posisi disesuaikan pada mobile */ } } @media (max-width: 480px) { .countdown-box { min-width: 50px; } .countdown-value { font-size: 1.3rem; } .countdown-separator { font-size: 1.3rem; } .promo-title { font-size: 1.2rem; } } /* Animasi kedip untuk seconds */ @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } .seconds-pulse .countdown-value { animation: pulse 1s infinite; } .flash-message { padding: 15px 20px; margin: 20px 0; border-left: 5px solid; border-radius: 4px; font-family: sans-serif; font-size: 14px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); animation: fadeInSlide 0.5s ease-in-out; } .flash-message.success { background-color: #e6ffed; border-color: #28a745; color: #1e4620; } .flash-message.error { background-color: #ffe6e6; border-color: #dc3545; color: #5a1e1e; } @keyframes fadeInSlide { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } .star-rating { display: inline-block; } .add-rating-stars { display: inline-flex; flex-direction: row-reverse; /* Makes hovering work from left to right */ cursor: pointer; } .add-rating-star { font-size: 28px; color: #7A4397; padding: 0 2px; transition: all 0.2s ease-in-out; } .add-rating-star:hover, .add-rating-star:hover~.star { content: '★'; color: #7A4397; } .add-rating-star.selected { content: '★'; color: #7A4397; } /* Optional: Add some hover animation */ .add-rating-star:hover { transform: scale(1.1); } .review-aspect-group { margin-bottom: 1.25rem; } .input-label { color: #333; } .aspect-options { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.5rem; cursor: pointer; } .aspect-option { display: flex; align-items: center; gap: 0.5rem; font-size: 14px; margin-left: 0.75rem; cursor: pointer; } .aspect-option input[type="radio"] { accent-color: #7A4397; cursor: pointer; } .aspect-dots .dot { width: 10px; height: 10px; border-radius: 50%; background-color: #e5e7eb; /* dot kosong = abu */ display: inline-block; } .aspect-dots .dot.filled { background-color: #7a4397; /* dot aktif = warna utama kamu */ } .label-text { flex: 1; } .aspect-rating-list { display: flex; flex-wrap: wrap; gap: 1.5rem; } .aspect-rating-item { display: flex; align-items: center; gap: 0.5rem; min-width: 0; /* Prevents flex items from overflowing */ } .aspect-rating-item span { font-weight: 500; font-size: 0.8rem; color: #333; /* white-space: nowrap; */ /* overflow: hidden; */ /* text-overflow: ellipsis; */ /* max-width: 150px; */ /* Limit text width for better layout */ } .review-aspect-dots { display: flex; gap: 2px; flex-shrink: 0; /* Prevents stars from shrinking */ } .review-aspect-dots .dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; background-color: #e5e7eb; /* abu-abu untuk dot kosong */ } .review-aspect-dots .dot.filled { background-color: #7a4397; /* warna utama kamu */ } /* Hover effects for better interactivity */ .review-aspect-dots .dot:hover { transform: scale(1.1); transition: transform 0.15s ease; } /* Mobile Responsive Styles */ @media (max-width: 768px) { .aspect-rating-list { background-color: rgb(248, 249, 249); gap: 1rem; padding: 0.75rem; border-radius: 8px; justify-content: space-between; } .aspect-rating-item { flex: 1; min-width: calc(50% - 0.5rem); /* 2 items per row on mobile */ gap: 0.4rem; } .aspect-rating-item span { font-size: 0.75rem; max-width: 100px; } .review-aspect-dots .dot { width: 10px; height: 10px; } } @media (max-width: 480px) { .aspect-rating-list { gap: 0.75rem; } .aspect-rating-item { min-width: 100%; /* Full width on very small screens */ justify-content: space-between; /* Spread aspect name and stars */ } .aspect-rating-item span { font-size: 0.7rem; max-width: 150px; } .review-aspect-dots { gap: 1px; /* Tighter spacing on small screens */ } .review-aspect-dots .dot { width: 8px; height: 8px; } } </style> <?php $data_utm = $this->session->userdata('data_utm'); // Daftar produk yang mendapatkan promo $promoProducts = [ 'femmegasm tapping tickling clitoral and vaginal vibrator', 'the smooth operator snazzy rechargeable clitoral vibrator', 'love to love sexy pills textured masturbator sleeve', 'vedo quiver wavy rechargeable g spot vibrator' ]; // Cek apakah produk saat ini termasuk dalam daftar promo $isPromoProduct = false; $currentProductTitle = strtolower($product->title); foreach ($promoProducts as $promoProduct) { if (strpos($currentProductTitle, $promoProduct) !== false) { $isPromoProduct = true; break; } } // Tampilkan banner promo hanya jika kondisi UTM dan produk terpenuhi if ( isset($data_utm['utm_campaign']) && strtolower($data_utm['utm_medium']) === 'mailinglist-21-03-2025' && strtolower($data_utm['utm_campaign']) === 'thr-21-03-2025' && $isPromoProduct ) : ?> <div class="promo-container"> <div class="promo-header"> <h1 class="promo-title">WAKTU MEPET JANGAN AMPE KEPEPET</h1> <p class="promo-subtitle">Ssst THR Rp. 650.000 Spesial Buat Kamu, Buruan Habek!!</p> </div> <div class="countdown-section"> <div class="countdown-label">CUMA BERLAKU: </div> <div class="countdown-timer"> <div class="countdown-box"> <span id="days" class="countdown-value">00</span> <span class="countdown-unit">Hari</span> </div> <div class="countdown-separator">:</div> <div class="countdown-box"> <span id="hours" class="countdown-value">00</span> <span class="countdown-unit">Jam</span> </div> <div class="countdown-separator">:</div> <div class="countdown-box"> <span id="minutes" class="countdown-value">00</span> <span class="countdown-unit">Menit</span> </div> <div class="countdown-separator">:</div> <div class="countdown-box seconds-pulse"> <span id="seconds" class="countdown-value">00</span> <span class="countdown-unit">Detik</span> </div> </div> </div> </div> <?php endif ?> <div class="product-container"> <div class="breadcrumb-container"> <nav aria-label="Breadcrumb"> <ol class="breadcrumb-list"> <li class="breadcrumb-item"> <a href="<?= base_url() ?>" class="breadcrumb-link"><?= lang('breadcrumb_home'); ?></a> </li> <li class="breadcrumb-item"> <a href="<?= base_url('product') ?>" class="breadcrumb-link">Produk</a> </li> <li class="breadcrumb-item"> <span class="breadcrumb-current"><?= $product->title ?></span> </li> </ol> </nav> </div> <?php if ($this->session->flashdata('message')): ?> <div class="flash-message success"> <?= $this->session->flashdata('message'); ?> </div> <?php endif; ?> <?php if ($this->session->flashdata('error')): ?> <div class="flash-message error"> <?= $this->session->flashdata('error'); ?> </div> <?php endif; ?> <div class="product-section-grid"> <!-- Popup untuk gambar --> <div class="popup-image-overlay"> <button class="arrow left-arrow"><</button> <!-- Tombol ke kiri --> <img class="popup-image" alt=""> <button class="arrow right-arrow">></button> <!-- Tombol ke kanan --> </div> <!-- Image Gallery --> <div class="image-gallery"> <?php if (!empty($product_images)): ?> <div class="main-image-container"> <img id="main-image" src="<?= base_url('uploads/product/' . $product_images[0]->image); ?>" alt="<?= htmlspecialchars($product_images[0]->alt); ?>" class="main-image"> <div class="overlay-stock" style="display: <?= $initial_product_detail->stock_available == 0 ? 'flex' : 'none' ?>;"> <span class="out-of-stock-text">Stok Habis</span> </div> </div> <div class="thumbnail-container" id="thumbnails"> <?php foreach ($product_images as $index => $image): ?> <div class="thumbnail-image-container"> <img src="<?= base_url('uploads/product/' . $image->image); ?>" alt="<?= htmlspecialchars($image->alt); ?>" class="thumbnail <?= isset($initial_product_detail->id) && $image->product_details_id === $initial_product_detail->id ? 'active' : ''; ?>" data-detail-id="<?= $image->product_details_id; ?>" data-index="<?= $index; ?>"> </div> <?php endforeach; ?> </div> <?php else: ?> <p>No images available</p> <?php endif; ?> </div> <!-- Product Info --> <div class="product-info"> <h1 class="product-title-main"><?= $product->title ?></h1> <div class="product-rating-stars"> <?php $rating = round($reviews->average_rating, 1); for ($i = 1; $i <= 5; $i++): $class = ($i <= floor($rating)) ? 'filled' : (($i - 0.5 == $rating) ? 'half' : 'empty'); ?> <svg class="star <?= $class ?>" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"> <?php if ($class === 'filled' || $class === 'half'): ?> <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" /> <?php endif; ?> <?php if ($class === 'empty'): ?> <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" /> <?php endif; ?> </svg> <?php endfor; ?> <!-- Display Rating Value and Count --> <span class="rating-value"><?= number_format($reviews->average_rating, 1) ?> </span> <span class="rating-count"> <?= $reviews->review_count > 0 ? "({$reviews->review_count} review)" : "(0 Review)" ?> </span> </div> <div class="share-wishlist-section"> <div class="share-icon"> <button id="shareBtn" aria-label="Share Product"> <i data-feather="share-2"></i> Share </button> </div> <div class="wishlist-icon"> <button id="wishlistBtn" aria-label="Add to Wishlist" data-is-wishlisted="<?= $is_wishlisted ? 'true' : 'false' ?>"> <i class="<?= $is_wishlisted ? 'fas fa-heart filled-heart' : 'far fa-heart outline-heart' ?> active"></i> Wishlist </button> </div> </div> <div class="variant-section" id="variant-section"> <h4><?= ucfirst(lang('product_select_variant')); ?>:</h4> <div class="variant-options" id="variant-options"> <?php foreach ($product_details as $detail): ?> <?php foreach ($detail->variants as $variant): ?> <div class="variant <?= $variant['product_attribute'] === 'Warna' ? 'color-variant' : 'text-variant' ?> <?= $detail->id === $initial_product_detail->id ? 'active' : '' ?>" data-product-id="<?= $detail->product_id ?>" data-detail-id="<?= $detail->id ?>" data-stock="<?= $detail->stock_available ?>" data-price="<?= $detail->current_price ?>" data-images='<?= json_encode($this->db->select('image')->from('product_images')->where(['product_id' => $product->id_products, 'product_details_id' => $detail->id])->where('status', 1)->limit(4)->order_by('priority', 'ASC')->get()->result_array()) ?>' data-product-attribute="<?= $variant['product_attribute'] ?>" data-attribute-detail="<?= $variant['attribute_detail'] ?>" data-variants="<?= $variant['variants'] ?>" data-sku="<?= $detail->sku ?>" data-attribute-detail-id="<?= $detail->attribute_detail_id ?>" style="<?= isset($variant['color_hex']) ? "background-color: {$variant['color_hex']}" : '' ?>" title="<?= htmlspecialchars($variant['attribute_detail']); ?>"> <?php if ($variant['product_attribute'] !== 'Warna'): ?> <?= htmlspecialchars($variant['attribute_detail']); ?> <?php endif; ?> </div> <?php endforeach; ?> <?php endforeach; ?> </div> </div> <?php if ($reseller): ?> <div class="reseller-media-link"> <?php if ($product->media_file_link): ?> <a href="<?= $product->media_file_link ?>" class="reseller-media-link-button" target="_blank"> <i data-feather="file" class="icon"></i> Download Media </a> <?php endif; ?> </div> <?php endif ?> <div class="tabs"> <div class="tab-buttons"> <button class="tab-btn active" onclick="showTab('deskripsi')"><?= ucfirst(lang('product_features')); ?></button> <!-- <button class="tab-btn" onclick="showTab('rincian-produk')"><?= ucfirst(lang('product_description')); ?></button> --> </div> </div> <div id="deskripsi" class="tab-content active"> <?php if ($this->session->userdata('site_lang') == 'english') { echo $product->description_en; } else { echo $product->description; } ?> </div> </div> <div class="purchase-section"> <div class="product-price"> <span class="current-product-price price" id="price"> IDR <?= number_format($detail->current_price, 0, ',', '.') ?> </span> <?php if (!empty($detail->msrp_price)): ?> <span class="msrp-price"> MSRP: IDR <?= number_format($detail->msrp_price, 0, ',', '.') ?> </span> <?php elseif (!empty($detail->original_price)): ?> <span class="original-product-price" id="original-price"> IDR <?= number_format($detail->original_price, 0, ',', '.') ?> </span> <?php endif; ?> </div> <?php if ($reseller): ?> <div class="reseller-product-stock"> Stok: <span id="reseller-stock">Pilih Varian</span> </div> <?php endif; ?> <div class="quantity-container"> <div class="quantity-selector"> <button class="qty-btn" onclick="updateQuantity(-1)">-</button> <input type="number" class="qty-input" value="1" min="1" max="<?= $initial_product_detail->stock_available ?>" id="quantity"> <button class="qty-btn" onclick="updateQuantity(1)">+</button> </div> </div> <div class="action-buttons"> <button class="button-cart" id="add-to-cart-button"><?= ucfirst(lang('product_add_to_cart')); ?></button> <!-- <button class="btn btn-secondary" id="direct-buy">Beli Langsung</button> --> </div> <div class="purchase-info"> <?php $current_price = $detail->current_price; if ($current_price > 1500000) { $discount_percent = 5; $discount_value = $current_price * 0.05; } else { $discount_percent = 3; $discount_value = $current_price * 0.03; } $formatted_discount = number_format($discount_value, 0, ',', '.'); ?> <div class="info-item" data-title="Gratis Ongkir! Hemat biaya pengiriman hingga Rp <?= $formatted_discount ?>. Pengiriman dilakukan setiap hari kerja (Senin - Jumat) sebelum pukul 16.00 WIB."> <img src="https://storage.googleapis.com/laciasmara-photos/laciaasmara_assets/laciasmara_benefit/Laciasmara_delivery.webp" alt="Free Shipping" class="info-icon"> <span><?= ucfirst(lang('product_free_shipping')); ?></span> </div> <div class="info-item" data-title="Dikemas dalam kertas khusus, dikirim oleh AAI, dinyatakan sebagai body massager/aksesoris/bola fitness."> <img src="https://storage.googleapis.com/laciasmara-photos/laciaasmara_assets/laciasmara_benefit/Laciasmara_packaging.webp" alt="Packaging" class="info-icon"> <span><?= ucfirst(lang('product_packaging')); ?></span> </div> <div class="info-item" data-title="Sebagai distributor resmi, kualitas produk terjamin untuk keamanan dan kenyamanan. Bergaransi 12 Bulan."> <img src="https://storage.googleapis.com/laciasmara-photos/laciaasmara_assets/laciasmara_benefit/Laciasmara_warranty.webp" alt="Warranty" class="info-icon"> <span><?= ucfirst(lang('product_guarantee')); ?></span> </div> </div> <!-- Modal --> <div id="info-modal" class="modal hidden"> <div class="modal-content"> <span class="close-button">×</span> <img id="modal-image" alt="Modal Image" class="modal-image" /> <p id="modal-text"></p> </div> </div> </div> </div> <div class="product-long-description"> <h3 class="product-long-description-header"><?= ucfirst(lang('product_description')); ?></h3> <div class="long-description"> <?php if ($this->session->userdata('site_lang') == 'english') { echo $product->long_description_en; } else { echo $product->long_description; } ?> </div> <div class="video-container"> <?php if ($product->product_video_link): ?> <div class="product-video"> <h3 class="product-video-header"><?= ucfirst(lang('product_video')) ?></h3> <div class="product-video-container"> <iframe width="100%" height="315" src="<?= $product->product_video_link ?>" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> </div> </div> <?php endif; ?> <?php if ($product->product_guide_link): ?> <div class="product-guide"> <h3 class="product-guide-header"><?= ucfirst(lang('product_guide')) ?></h3> <div class="product-guide-container"> <iframe width="100%" height="315" src="<?= $product->product_guide_link ?>" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> </div> </div> <?php endif; ?> </div> </div> <!-- Suggestion Product --> <?php if ($suggested_products): ?> <div class="product-suggestion-container"> <div class="product-suggestion"> <h3 class="product-suggestion-header"><?= ucfirst(lang('product_suggestion_header')); ?></h3> <div class="product-grid"> <?php foreach ($suggested_products as $suggested_product): ?> <div class="product-card"> <a href="<?= base_url('product/' . $suggested_product['alias']) ?>" class="product-link"> <div class="product-image-container"> <div class="image-wrapper"> <?php if ($suggested_product['is_completely_sold_out']): ?> <div class="soldout-wrapper"> <img class="soldout-image" src="<?= base_url() . 'assets/frontend/img/soldout-new.png'; ?>" alt="Sold Out"> </div> <?php elseif ($suggested_product['has_badges'] && !empty($suggested_product['badges'])): ?> <div class="product-badges-container"> <?php foreach ($suggested_product['badges'] as $badge): ?> <div class="product-badge priority-<?= $badge['priority'] ?> <?= $badge['css_class'] ?>" style="<?= $badge['style'] ?>"> <?php if ($badge['has_icon'] && !empty($badge['icon'])): ?> <span class="badge-icon emoji"><?= $badge['icon'] ?></span> <?php endif; ?> <span class="badge-text"><?= htmlspecialchars($badge['name']) ?></span> </div> <?php endforeach; ?> </div> <?php endif; ?> <img src="<?= base_url('uploads/product/' . $suggested_product['image']) ?>" alt="<?= $suggested_product['title'] ?>" class="product-image first_image" loading="lazy"> <img src="<?= base_url('uploads/product/' . $suggested_product['image_secondary']) ?>" alt="<?= $suggested_product['title'] ?>" class="product-image secondary-image" loading="lazy"> </div> </div> <div class="product-info"> <h3 class="product-title"><?= htmlspecialchars($suggested_product['title'], ENT_QUOTES) ?></h3> <div class="product-price"> <span class="current-price">IDR <?= number_format($suggested_product['current_price'], 0, ',', '.') ?></span> <?php if (!empty($suggested_product['msrp_price'])): ?> <span class="msrp-price">MSRP: IDR <?= number_format($suggested_product['msrp_price'], 0, ',', '.') ?></span> <?php elseif (!empty($suggested_product['original_price'])): ?> <span class="original-price">IDR <?= number_format($suggested_product['original_price'], 0, ',', '.') ?></span> <?php endif; ?> </div> <!-- <?php if (!empty($suggested_product['variants'])): ?> <div class="product-variants"> <span class="variant-label"><?= htmlspecialchars($suggested_product['variants'][0], ENT_QUOTES) ?></span> </div> <?php endif; ?> --> <div class="rating-section"> <i class="fas fa-star"></i> <span class="review-text"> <?= htmlspecialchars($suggested_product['average_rating'] ?? '0', ENT_QUOTES) ?> | <?= htmlspecialchars($suggested_product['total_reviews'] ?? '0', ENT_QUOTES) ?> Reviews </span> </div> </div> </a> <form class="wishlist-form" data-is-wishlisted="<?= $suggested_product['is_wishlisted'] ? 'true' : 'false' ?>"> <input type="hidden" name="<?= $this->security->get_csrf_token_name(); ?>" value="<?= $this->security->get_csrf_hash(); ?>" /> <input type="hidden" name="product_id" value="<?= $suggested_product['id'] ?>"> <input type="hidden" name="product_detail_id" value="<?= $suggested_product['id_detail'] ?>"> <input type="hidden" name="variant" value="<?= $suggested_product['variants'][0] ?? 'No variants' ?>"> <button type="submit" class="wishlist-button"> <div class="heart-container"> <i class="<?= $suggested_product['is_wishlisted'] ? 'fas fa-heart filled-heart' : 'far fa-heart outline-heart' ?> active"></i> </div> </button> </form> <form class="add-to-cart-form"> <input type="hidden" name="<?= $this->security->get_csrf_token_name(); ?>" value="<?= $this->security->get_csrf_hash(); ?>" /> <input type="hidden" name="product_id" value="<?= $suggested_product['id'] ?>"> <input type="hidden" name="product_detail_id" value="<?= $suggested_product['id_detail'] ?>"> <input type="hidden" name="product_variant" value="<?= htmlspecialchars($suggested_product['variants'][0] ?? 'No variants', ENT_QUOTES) ?>"> <input type="hidden" name="product_name" value="<?= htmlspecialchars($suggested_product['title'], ENT_QUOTES) ?>"> <input type="hidden" name="product_image" value="<?= base_url('uploads/product/' . $suggested_product['image']) ?>"> <input type="hidden" name="qty" value="1"> <input type="hidden" name="price" value="<?= $suggested_product['current_price'] ?>"> <input type="hidden" name="attribute_detail_id" value="<?= $suggested_product['attribute_detail_id'] ?>"> <input type="hidden" name="sku" value="<?= $suggested_product['sku'] ?>"> <button type="submit" class="add-to-cart-button"> <i data-feather="shopping-cart" class="shopping-cart-icon"></i> Add to Cart </button> </form> </div> <?php endforeach; ?> </div> </div> </div> <?php endif; ?> <!-- Review --> <div class="reviews-section"> <!-- Review Summary --> <div class="review-summary"> <div class="review-header"> <div> <?php $rating_breakdown = []; if ($reviews && $reviews->review_count > 0) { $rating_breakdown = [ 5 => $reviews->rating_5, 4 => $reviews->rating_4, 3 => $reviews->rating_3, 2 => $reviews->rating_2, 1 => $reviews->rating_1 ]; } ?> <h3 class="review-title"><?= ucfirst(lang('product_review')); ?></h3> <?php if ($reviews && $reviews->review_count > 0): ?> <div class="review-content"> <!-- Left Side - Average Rating --> <div class="rating-overview"> <div class="average-rating"> <?= number_format($reviews->average_rating, 1) ?> </div> <div class="rating-stars"> <?php for ($i = 1; $i <= 5; $i++): ?> <svg class="star <?= $i <= round($reviews->average_rating) ? 'filled' : 'empty' ?>" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"> <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" /> </svg> <?php endfor; ?> </div> <div class="total-reviews"> <?= $reviews->review_count ?> </div> <div class="reviews-text"> <?= ucfirst(lang('product_total_review')) ?> </div> </div> <!-- Right Side - Rating Breakdown --> <div class="rating-breakdown"> <!-- <div class="breakdown-title">Detail Rating</div> --> <?php foreach ($rating_breakdown as $rating => $count): ?> <?php $percentage = $reviews->review_count > 0 ? ($count / $reviews->review_count) * 100 : 0; ?> <div class="rating-row"> <div class="rating-label"> <svg class="star" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"> <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" /> </svg> <?= $rating ?> </div> <div class="progress-container"> <div class="progress-bar" style="width: <?= number_format($percentage, 1) ?>%;"> </div> </div> <div class="rating-count"> <?= $count ?> </div> </div> <?php endforeach; ?> </div> </div> <button id="toggleAddReviewBtn" class="add-review-button"> <?= ucfirst(lang('add_review_title')); ?> </button> <!-- Filter Section --> <div class="review-filter-container"> <div class="filter-header"> <h3 class="filter-title"> <svg class="filter-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.207A1 1 0 013 6.5V4z"></path> </svg> <?= ucfirst(lang('product_review_filter_title')) ?> </h3> <button class="filter-reset" onclick="resetAllFilters()">Reset Filter</button> </div> <div class="filter-options"> <!-- Sort Filter --> <div class="filter-group"> <label class="filter-label"> <?= ucfirst(lang('product_review_filter_sort_by')) ?></label> <select class="filter-select" id="sortFilter" onchange="applyFilters()"> <option value="newest"><?= ucfirst(lang('product_review_filter_sort_by_latest')) ?></option> <option value="oldest"><?= ucfirst(lang('product_review_filter_sort_by_longest')) ?></option> <option value="highest"><?= ucfirst(lang('product_review_filter_sort_by_high')) ?></option> <option value="lowest"><?= ucfirst(lang('product_review_filter_sort_by_low')) ?></option> </select> </div> <!-- Rating Filter --> <div class="filter-group"> <label class="filter-label"><?= ucfirst(lang('product_review_filter_rating')) ?></label> <div class="star-filter-buttons"> <button class="star-filter-btn" data-rating="all" onclick="filterByRating('all')"> <?= ucfirst(lang('product_review_filter_rating_all')) ?> </button> <button class="star-filter-btn" data-rating="5" onclick="filterByRating(5)"> <svg class="star filled" viewBox="0 0 20 20" fill="currentColor"> <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" /> </svg> 5 </button> <button class="star-filter-btn" data-rating="4" onclick="filterByRating(4)"> <svg class="star filled" viewBox="0 0 20 20" fill="currentColor"> <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" /> </svg> 4+ </button> <button class="star-filter-btn" data-rating="3" onclick="filterByRating(3)"> <svg class="star filled" viewBox="0 0 20 20" fill="currentColor"> <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" /> </svg> 3+ </button> <button class="star-filter-btn" data-rating="2" onclick="filterByRating(2)"> <svg class="star filled" viewBox="0 0 20 20" fill="currentColor"> <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" /> </svg> 2+ </button> <button class="star-filter-btn" data-rating="1" onclick="filterByRating(1)"> <svg class="star filled" viewBox="0 0 20 20" fill="currentColor"> <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" /> </svg> 1 </button> </div> </div> </div> </div> <div class="results-info" id="resultsInfo"> <span class="results-count">Menampilkan <strong id="visibleCount">0</strong> dari <strong id="totalCount">0</strong> review</span> <span id="activeFilters"></span> </div> <!-- Review List --> <?php if (!empty($review_details)): ?> <div class="review-list" id="reviewList"> <?php foreach ($review_details as $review): ?> <div class="review-item" data-rating="<?= $review->rating ?>" data-date="<?= date('Y-m-d', strtotime($review->review_date)) ?>"> <!-- Review Header --> <div class="review-item-header"> <div class="reviewer-info"> <!-- Reviewer Avatar --> <div class="reviewer-avatar"> <?= strtoupper(substr($review->display_name, 0, 1)) ?> </div> <div class="reviewer-details"> <h4 class="reviewer-name"> <?= htmlspecialchars($review->display_name) ?> <?php if ($review->is_verified_purchase == 1): ?> <span>• Verified</span> <?php endif; ?> </h4> </div> </div> <!-- Review Meta --> <div class="review-meta"> <!-- Rating Stars --> <div class="review-rating-stars"> <?php for ($i = 1; $i <= 5; $i++): ?> <svg class="star <?= $i <= $review->rating ? 'filled' : 'empty' ?>" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"> <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" /> </svg> <?php endfor; ?> <!-- <span class="rating-value"><?= $review->rating ?>/5</span> --> </div> <!-- Review Date --> <span class="review-date"> <?= date('d M Y', strtotime($review->review_date)) ?> </span> </div> </div> <!-- Review Subject --> <?php if (!empty($review->subject)): ?> <h5 class="review-subject"><?= $review->subject ?></h5> <?php endif; ?> <!-- Review Text --> <p class="review-text"><?= nl2br($review->review) ?></p> <div class="review-actions" style="margin-top: 1rem; padding-top: 1rem; border-top: 1px solid #f3f4f6; display: flex; gap: 1rem; align-items: center;"> <?php if (!empty($review->aspect_ratings)): ?> <div class="aspect-rating-list"> <?php foreach ($review->aspect_ratings as $aspect): ?> <div class="aspect-rating-item"> <!-- <span title="<?= htmlspecialchars($aspect->aspect_name); ?>"> <?= htmlspecialchars($aspect->aspect_name); ?> </span> --> <span title="<?= ucwords(str_replace('_', ' ', htmlspecialchars($aspect->aspect_title))); ?>"> <?= ucwords(str_replace('_', ' ', $aspect->aspect_title)); ?> </span> <div class="review-aspect-dots" style="display: flex; gap: 0.25rem;"> <?php for ($i = 1; $i <= 5; $i++): ?> <span class="dot <?= $i <= $aspect->rating ? 'filled' : 'empty' ?>"></span> <?php endfor; ?> </div> </div> <?php endforeach; ?> </div> <?php endif; ?> <!-- <button class="helpful-btn" style="background: none; border: none; color: #6b7280; font-size: 0.875rem; cursor: pointer; display: flex; align-items: center; gap: 0.25rem;"> <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 10h4.764a2 2 0 011.789 2.894l-3.5 7A2 2 0 0115.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20m7-10V5a2 2 0 00-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 01-2-2v-6a2 2 0 012-2h2.5" /> </svg> Membantu </button> --> <!-- <span style="color: #d1d5db;">•</span> --> <!-- <button class="report-btn" style="background: none; border: none; color: #6b7280; font-size: 0.875rem; cursor: pointer;"> Laporkan </button> --> </div> </div> <?php endforeach; ?> </div> <?php endif; ?> <div class="no-results" id="noResults" style="display: none;"> <svg class="no-results-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.172 16.172a4 4 0 015.656 0M9 12h6m-6-4h6m2 5.291A7.962 7.962 0 0112 15c-2.34 0-4.291-1.007-5.824-2.618M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2z"></path> </svg> <h3>Tidak ada ulasan yang sesuai</h3> <p>Coba ubah filter atau reset untuk melihat semua ulasan</p> </div> <?php else: ?> <div class="no-reviews"> <svg class="no-reviews-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z" /> </svg> <div class="no-reviews-title"> <?= ucfirst(lang('product_no_review')) ?> </div> <div class="no-reviews-text"> <?= ucfirst(lang('submit_product_review')) ?> </div> <button id="toggleAddReviewBtn" class="add-review-button"> <?= ucfirst(lang('add_review_title')); ?> </button> </div> <?php endif; ?> </div> </div> </div> </div> <!-- Add Review Section --> <div class="add-review-container"> <h3 class="add-review-title"><?= ucfirst(lang('add_review_title')); ?></h3> <form action="<?= base_url('product_review/add_reviews'); ?>" method="POST" class="review-form"> <input type="hidden" name="<?= $this->security->get_csrf_token_name(); ?>" value="<?= $this->security->get_csrf_hash(); ?>" /> <input type="hidden" name="product_id" value="<?= $product->id_products ?>"> <input type="hidden" name="product_alias" value="<?= $product->alias ?>"> <div class="input-group"> <label for="display_name" class="input-label"><?= ucfirst(lang('label_name')); ?></label> <input type="text" id="display_name" name="display_name" class="input-field" required> </div> <div class="input-group"> <label for="rating" class="input-label"><?= ucfirst(lang('label_rating')); ?></label> <div class="star-rating"> <input type="hidden" name="rating" id="rating" value="" required> <div class="add-rating-stars"> <?php for ($i = 5; $i >= 1; $i--): ?> <span class="add-rating-star" data-rating="<?= $i ?>">☆</span> <?php endfor; ?> </div> </div> </div> <?php if (!empty($review_aspects)): ?> <div class="input-group"> <?php foreach ($review_aspects as $aspect): ?> <div class="input-group review-aspect-group"> <label class="input-label"><?= $aspect['display_name']; ?></label> <div class="aspect-options"> <?php foreach ($aspect['options'] as $option): ?> <label class="aspect-option" style="display: flex; align-items: center; gap: 0.5rem;"> <input type="radio" name="aspect_rating[<?= $aspect['id']; ?>]" value="<?= $option['rating_value']; ?>" required> <span class="aspect-dots" style="display: flex; gap: 0.2rem;"> <?php for ($i = 1; $i <= 5; $i++): ?> <span class="dot <?= $i <= $option['rating_value'] ? 'filled' : 'empty' ?>"></span> <?php endfor; ?> </span> <span class="label-text"><?= $option['label']; ?></span> </label> <?php endforeach; ?> </div> </div> <?php endforeach; ?> </div> <?php endif; ?> <div class="input-group"> <label for="subject" class="input-label"><?= ucfirst(lang('label_subject')); ?></label> <input type="text" id="subject" name="subject" class="input-field"> </div> <div class="input-group"> <label for="review" class="input-label"><?= ucfirst(lang('label_review')); ?></label> <textarea id="review" name="review" rows="4" class="input-field" required></textarea> </div> <div class="add-submit-container"> <button type="submit" class="add-submit-button"> <?= ucfirst(lang('submit_review')); ?> </button> </div> </form> </div> <button class="fab-cart" onclick="document.getElementById('add-to-cart-button').click()" aria-label="Add to Cart"> <i data-feather="shopping-cart"></i> </button> </div> <!-- Sidebar --> <div id="cart-sidebar" class="cart-sidebar"> <div class="cart-sidebar-header"> <h3>Berhasil Menambahkan ke Keranjang!</h3> <!-- <h3>Berhasil Menambahkan ke Keranjang <span id="cart-sidebar-count">0</span></h3> --> <button id="close-sidebar" class="close-sidebar">×</button> </div> <div id="cart-items-sidebar" class="cart-items-sidebar"> <!-- Cart items will be injected here --> </div> <div class="cart-sidebar-footer"> <button class="cta-button-sidebar" id="continue-shopping">Lanjut Belanja</button> <button class="cta-button-sidebar" id="go-to-checkout">Beli</button> </div> <div class="cart-sidebar-suggestions"> <h4 class="sidebar-suggestions-title">Beli Barengan</h4> <div id="suggested-sidebar-products" class="sidebar-suggested-products-grid"> </div> </div> </div> <div id="fab-tutorial-overlay" class="fab-overlay"> <div class="fab-tooltip"> <p><?= ucfirst(lang('fab_add_to_cart')); ?></p> </div> <button id="fab-tutorial-close" class="fab-tutorial-btn"><?= ucfirst(lang('fab_understand')); ?></button> </div> <?php // Hitung apakah promo masih aktif berdasarkan waktu $startTime = strtotime('March 21, 2025 17:00:00'); $endTime = $startTime + (168 * 60 * 60); // 168 jam (7 hari) $currentTime = time(); $isTimeActive = ($currentTime <= $endTime); // Cek kondisi UTM dan waktu $isPromoActive = ( isset($data_utm['utm_campaign']) && strtolower($data_utm['utm_medium']) === 'mailinglist-21-03-2025' && strtolower($data_utm['utm_campaign']) === 'thr-21-03-2025' && $isTimeActive ) ? 'true' : 'false'; ?> <script> let selectedProductId = null; let selectedVariant = null; let selectedDetailId = null; let selectedStock = null; let selectedVariantPrice = null; let selectedProductName = null; let selectedVariantImage = null; let selectedSKU = null; let selectedAttributeDetailId = null; const promoProducts = [{ name: "femmegasm tapping tickling clitoral and vaginal vibrator", originalPrice: 1350000, promoPrice: 1215000, utmContent: "banner-femme" }, { name: "the smooth operator snazzy rechargeable clitoral vibrator", originalPrice: 1300000, promoPrice: 910000, utmContent: "banner-snazzy" }, { name: "love to love sexy pills textured masturbator sleeve", originalPrice: 220000, promoPrice: 198000, utmContent: "banner-sexypills" }, { name: "vedo quiver wavy rechargeable g spot vibrator", originalPrice: 1000000, promoPrice: 900000, utmContent: "banner-quiver" } ]; const isPromoActive = <?= $isPromoActive ?>; let currentRatingFilter = 'all'; let currentSortFilter = 'newest'; // Initial Variant document.addEventListener("DOMContentLoaded", function() { const progressBars = document.querySelectorAll('.progress-bar'); const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver(function(entries) { entries.forEach(entry => { if (entry.isIntersecting) { const progressBar = entry.target; const targetWidth = progressBar.style.width; progressBar.style.width = '0%'; setTimeout(() => { progressBar.style.width = targetWidth; }, 100); observer.unobserve(progressBar); } }); }, observerOptions); progressBars.forEach(bar => { observer.observe(bar); }); const toggleBtn = document.getElementById('toggleAddReviewBtn'); const reviewForm = document.querySelector('.add-review-container'); toggleBtn.addEventListener('click', function() { if (reviewForm.style.display === 'none' || !reviewForm.style.display) { reviewForm.style.display = 'block'; reviewForm.scrollIntoView({ behavior: 'smooth' }); } else { reviewForm.style.display = 'none'; } }); // startCountdown('March 21, 2025 17:00:00', 168); initializeFilters(); updateResultsInfo(); // setTimeout(function() { // const messages = document.querySelectorAll('.flash-message'); // if (messages) { // messages.forEach(el => el.style.display = 'none'); // } // }, 4000); const defaultVariant = document.querySelector('.variant.active'); if (defaultVariant) { defaultVariant.click(); let initialPrice = <?= $initial_product_detail->current_price ?>; const initialProductName = "<?= htmlspecialchars($product->title) ?>"; // Terapkan harga promo jika memenuhi syarat // initialPrice = applyPromoPrice(initialProductName, initialPrice); const initialDetail = { productId: "<?= $initial_product_detail->product_id ?>", detailId: "<?= $initial_product_detail->id ?>", stock: <?= $initial_product_detail->stock_available ?>, price: initialPrice, sku: "<?= $initial_product_detail->sku ?>", attributeDetailId: "<?= $initial_variant->attribute_detail_id ?>", variant: { attribute: "<?= $initial_variant['product_attribute'] ?>", detail: "<?= $initial_variant['attribute_detail'] ?>", variants: "<?= $initial_variant['variants'] ?>" }, productName: "<?= htmlspecialchars($product->title) ?>", images: <?= json_encode($this->db->select('image')->from('product_images')->where(['product_id' => $product->id_products, 'product_details_id' => $initial_product_detail->id])->where('status', 1)->limit(4)->order_by('priority', 'ASC')->get()->result_array()) ?> }; selectedProductId = initialDetail.productId; selectedDetailId = initialDetail.detailId; selectedVariant = initialDetail.variant.variants; selectedStock = initialDetail.stock; selectedVariantPrice = initialDetail.price; selectedProductName = initialDetail.productName; selectedVariantImage = initialDetail.images; selectedSKU = initialDetail.sku; selectedAttributeDetailId = initialDetail.attributeDetailId const priceElement = document.getElementById('price'); if (priceElement) { priceElement.textContent = 'IDR ' + new Intl.NumberFormat('id-ID').format(selectedVariantPrice); } // Tampilkan harga asli jika promo aktif // const originalPrice = displayOriginalPrice(initialProductName); // if (originalPrice) { // const originalPriceElement = document.getElementById('original-price'); // if (originalPriceElement) { // originalPriceElement.textContent = 'IDR ' + new Intl.NumberFormat('id-ID').format(originalPrice); // originalPriceElement.classList.remove('hidden'); // } else { // // Jika elemen tidak ada, tambahkan elemen harga asli // const priceContainer = document.querySelector('.product-price'); // const originalPriceSpan = document.createElement('span'); // originalPriceSpan.id = 'original-price'; // originalPriceSpan.className = 'original-price'; // originalPriceSpan.textContent = 'IDR ' + new Intl.NumberFormat('id-ID').format(originalPrice); // priceContainer.appendChild(originalPriceSpan); // } // } } }); // Info Items (Shipping, Warranty) document.addEventListener("DOMContentLoaded", function() { const infoItems = document.querySelectorAll(".info-item"); const modal = document.getElementById("info-modal"); const modalText = document.getElementById("modal-text"); const modalImage = document.getElementById("modal-image"); const closeButton = document.querySelector(".close-button"); infoItems.forEach(item => { item.addEventListener("click", function() { const title = item.getAttribute("data-title"); const imgSrc = item.querySelector(".info-icon").src; modalText.textContent = title; modalImage.src = imgSrc; modal.classList.remove("hidden"); modal.style.display = "flex"; }); }); closeButton.addEventListener("click", function() { modal.classList.add("hidden"); modal.style.display = "none"; }); modal.addEventListener("click", function(e) { if (e.target === modal) { modal.classList.add("hidden"); modal.style.display = "none"; } }); }); function initializeFilters() { // Set initial active state for rating filter const allRatingBtn = document.querySelector('[data-rating="all"]'); if (allRatingBtn) { allRatingBtn.classList.add('active'); } // Apply initial filters applyFilters(); } function filterByRating(rating) { currentRatingFilter = rating; // Update active button state document.querySelectorAll('.star-filter-btn').forEach(btn => { btn.classList.remove('active'); }); const activeBtn = document.querySelector(`[data-rating="${rating}"]`); if (activeBtn) { activeBtn.classList.add('active'); } applyFilters(); } function applyFilters() { const sortValue = document.getElementById('sortFilter'); if (sortValue) { currentSortFilter = sortValue.value; } else { currentSortFilter = 'newest'; } const reviewItems = Array.from(document.querySelectorAll('.review-item')); // First, filter by rating reviewItems.forEach(item => { const itemRating = parseInt(item.getAttribute('data-rating')); let shouldShow = true; if (currentRatingFilter !== 'all') { const filterRating = parseInt(currentRatingFilter); if (filterRating === 5) { shouldShow = itemRating === 5; } else if (filterRating === 1) { shouldShow = itemRating === 1; } else { // For 2+, 3+, 4+ - show items with rating >= filter rating shouldShow = itemRating >= filterRating; } } if (shouldShow) { item.classList.remove('filtered-out'); item.classList.add('filtered-in'); } else { item.classList.add('filtered-out'); item.classList.remove('filtered-in'); } }); // Then sort the visible items const visibleItems = reviewItems.filter(item => !item.classList.contains('filtered-out')); sortReviews(visibleItems, sortValue); // Update results info updateResultsInfo(); // Show/hide no results message toggleNoResultsMessage(visibleItems.length === 0); } function sortReviews(items, sortType) { const container = document.getElementById('reviewList'); items.sort((a, b) => { switch (sortType) { case 'newest': return new Date(b.getAttribute('data-date')) - new Date(a.getAttribute('data-date')); case 'oldest': return new Date(a.getAttribute('data-date')) - new Date(b.getAttribute('data-date')); case 'highest': return parseInt(b.getAttribute('data-rating')) - parseInt(a.getAttribute('data-rating')); case 'lowest': return parseInt(a.getAttribute('data-rating')) - parseInt(b.getAttribute('data-rating')); default: return 0; } }); // Re-append sorted items to container items.forEach(item => { container.appendChild(item); }); } function updateResultsInfo() { const totalItems = document.querySelectorAll('.review-item').length; const visibleItems = document.querySelectorAll('.review-item:not(.filtered-out)').length; const totalCountElement = document.getElementById('totalCount'); if (totalCountElement) { totalCountElement.textContent = totalItems; } const visibleCountElement = document.getElementById('visibleCount'); if (visibleCountElement) { visibleCountElement.textContent = visibleItems; } // Update active filters display const activeFiltersEl = document.getElementById('activeFilters'); if (!activeFiltersEl) { return; } const activeFilters = []; if (currentRatingFilter !== 'all') { if (currentRatingFilter === '5') { activeFilters.push('Rating: 5 bintang'); } else if (currentRatingFilter === '1') { activeFilters.push('Rating: 1 bintang'); } else { activeFilters.push(`Rating: ${currentRatingFilter}+ bintang`); } } const sortLabels = { 'newest': 'Terbaru', 'oldest': 'Terlama', 'highest': 'Rating Tertinggi', 'lowest': 'Rating Terendah' }; if (currentSortFilter !== 'newest') { activeFilters.push(`Urutan: ${sortLabels[currentSortFilter]}`); } activeFiltersEl.textContent = activeFilters.length > 0 ? `Filter aktif: ${activeFilters.join(', ')}` : ''; } function toggleNoResultsMessage(show) { const noResultsEl = document.getElementById('noResults'); const reviewListEl = document.getElementById('reviewList'); if (noResultsEl && reviewListEl) { if (show) { noResultsEl.style.display = 'block'; reviewListEl.style.display = 'none'; } else { noResultsEl.style.display = 'none'; reviewListEl.style.display = 'flex'; } } } function resetAllFilters() { // Reset rating filter currentRatingFilter = 'all'; document.querySelectorAll('.star-filter-btn').forEach(btn => { btn.classList.remove('active'); }); document.querySelector('[data-rating="all"]').classList.add('active'); // Reset sort filter currentSortFilter = 'newest'; document.getElementById('sortFilter').value = 'newest'; // Show all items document.querySelectorAll('.review-item').forEach(item => { item.classList.remove('filtered-out'); item.classList.add('filtered-in'); }); // Apply filters to resort applyFilters(); } function formatDate(dateString) { const date = new Date(dateString); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des']; return `${date.getDate()} ${months[date.getMonth()]} ${date.getFullYear()}`; } // Notyf const notyf = new Notyf({ duration: 3000, position: { x: 'right', y: 'top' }, types: [{ type: 'success', background: '#7A4397', icon: { className: 'fas fa-check', tagName: 'i', color: 'white' } }, { type: 'error', background: '#dc3545', icon: { className: 'fas fa-times', tagName: 'i', color: 'white' } } ] }); // Tab Description function showTab(tabId) { document.querySelectorAll('.tab-content').forEach(content => { content.classList.remove('active'); }); document.querySelectorAll('.tab-btn').forEach(btn => { btn.classList.remove('active'); }); document.getElementById(tabId).classList.add('active'); document.querySelector(`[onclick="showTab('${tabId}')"]`).classList.add('active'); } function numberFormat(number, decimals = 0, decPoint = ',', thousandsSep = '.') { number = parseFloat(number).toFixed(decimals); const parts = number.split('.'); parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSep); return parts.join(decPoint); } // Inisialisasi harga awal const initialPrice = <?= $initial_product_detail->price ?>; // Update QTY function updateQuantity(change) { // Elemen input kuantitas const quantityInput = document.getElementById('quantity'); const priceElement = document.getElementById('price'); const maxQuantity = parseInt(quantityInput.getAttribute('max')); let currentQuantity = parseInt(quantityInput.value); currentQuantity += change; if (currentQuantity < 1) { currentQuantity = 1; } else if (currentQuantity > maxQuantity) { currentQuantity = maxQuantity; notyf.error(`Jumlah yang Kamu pilih terlalu banyak. Kurangi jumlahnya atau pilih varian lain.`); } quantityInput.value = currentQuantity; } // Tangkap perubahan langsung pada input (jika pengguna mengetik manual) document.getElementById('quantity').addEventListener('input', function() { const maxQuantity = parseInt(this.getAttribute('max')); const minQuantity = parseInt(this.getAttribute('min')); let currentQuantity = parseInt(this.value); if (isNaN(currentQuantity) || currentQuantity < minQuantity) { currentQuantity = minQuantity; } else if (currentQuantity > maxQuantity) { currentQuantity = maxQuantity; notyf.error(`Jumlah yang Kamu pilih terlalu banyak. Kurangi jumlahnya atau pilih varian lain.`); } this.value = currentQuantity; }); // Update thumbnails const thumbnails = document.querySelectorAll('.thumbnail'); const mainImage = document.querySelector('.main-image'); thumbnails.forEach(thumbnail => { thumbnail.addEventListener('click', () => { const newSrc = thumbnail.getAttribute('src'); const newAlt = thumbnail.getAttribute('alt'); mainImage.setAttribute('src', newSrc); mainImage.setAttribute('alt', newAlt); thumbnails.forEach(img => img.classList.remove('active')); thumbnail.classList.add('active'); const detailId = this.dataset.detailId; const matchingVariant = document.querySelector(`.variant[data-detail-id="${detailId}"]`); if (matchingVariant) { const stock = parseInt(matchingVariant.dataset.stock); mainOverlay.style.display = stock === 0 ? 'flex' : 'none'; } }); }); document.addEventListener('DOMContentLoaded', function() { // Function untuk update overlay stok function updateStockOverlay(stock, container) { const overlay = container.querySelector('.overlay-stock'); if (overlay) { overlay.style.display = stock === 0 ? 'flex' : 'none'; } } // Function untuk membuat thumbnail container dengan overlay function createThumbnailContainer(img, baseUrl, isActive = false, stock = 0) { const container = document.createElement('div'); container.className = 'main-image-container'; const thumbnail = document.createElement('img'); thumbnail.src = `${baseUrl}/${img.image}`; thumbnail.alt = img.title || ''; thumbnail.className = `thumbnail${isActive ? ' active' : ''}`; const overlay = document.createElement('div'); overlay.className = 'overlay-stock'; overlay.innerHTML = '<span class="out-of-stock-text">Stok Habis</span>'; overlay.style.display = stock === 0 ? 'flex' : 'none'; container.appendChild(thumbnail); container.appendChild(overlay); return container; } // Event listener untuk variants document.querySelectorAll('.variant').forEach(variant => { variant.addEventListener('click', function() { // Update active state untuk variants dengan attribute yang sama const currentAttribute = this.dataset.productAttribute; document.querySelectorAll('.variant').forEach(v => { if (v.dataset.productAttribute === currentAttribute) { v.classList.remove('active'); } }); this.classList.add('active'); // Ambil data dari variant yang dipilih selectedProductId = this.dataset.productId selectedDetailId = this.dataset.detailId; selectedVariant = this.dataset.variants; selectedStock = parseInt(this.dataset.stock); selectedVariantPrice = parseFloat(this.dataset.price); selectedProductName = document.querySelector('.product-title-main').textContent.trim(); selectedVariantImage = JSON.parse(this.dataset.images); selectedSKU = this.dataset.sku; selectedAttributeDetailId = this.dataset.attributeDetailId; const detailId = this.dataset.detailId; const price = parseFloat(this.dataset.price); const stock = parseInt(this.dataset.stock); const images = JSON.parse(this.dataset.images); let variantPrice = parseFloat(this.dataset.price); // Terapkan harga promo jika kondisi terpenuhi // variantPrice = applyPromoPrice(selectedProductName, variantPrice); // Update selected price dengan harga yang sudah diproses selectedVariantPrice = variantPrice; const quantityInput = document.getElementById('quantity'); if (quantityInput) { // Update max attribute berdasarkan stock variant yang dipilih quantityInput.setAttribute('max', selectedStock); // Pastikan nilai quantity saat ini tidak melebihi stock baru const currentQuantity = parseInt(quantityInput.value) || 1; if (currentQuantity > selectedStock) { quantityInput.value = selectedStock; } } // Update main image dan overlay-nya const mainImageContainer = document.querySelector('.main-image-container'); const mainImage = document.getElementById('main-image'); const baseUrl = "<?= base_url('uploads/product'); ?>"; if (images && images.length) { // Update main image mainImage.src = `${baseUrl}/${images[0].image}`; mainImage.alt = images[0].title || ''; updateStockOverlay(stock, mainImageContainer); // Update thumbnails const thumbnailsContainer = document.getElementById('thumbnails'); thumbnailsContainer.innerHTML = ''; images.forEach((img, index) => { const thumbnailContainer = createThumbnailContainer(img, baseUrl, index === 0, stock); thumbnailsContainer.appendChild(thumbnailContainer); const thumbnail = thumbnailContainer.querySelector('.thumbnail'); thumbnail.addEventListener('click', () => { // Update main image ketika thumbnail diklik mainImage.src = thumbnail.src; mainImage.alt = thumbnail.alt; // Update active state thumbnails document.querySelectorAll('.thumbnail').forEach(t => t.classList.remove('active')); thumbnail.classList.add('active'); }); }); } const priceElement = document.getElementById('price'); if (priceElement) { priceElement.textContent = 'IDR ' + new Intl.NumberFormat('id-ID', { style: 'decimal', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(selectedVariantPrice); } const originalPrice = displayOriginalPrice(selectedProductName); const originalPriceElement = document.getElementById('original-price'); if (originalPrice) { if (originalPriceElement) { originalPriceElement.textContent = 'IDR ' + new Intl.NumberFormat('id-ID').format(originalPrice); originalPriceElement.classList.remove('hidden'); } else { // Jika elemen tidak ada, tambahkan elemen harga asli const priceContainer = document.querySelector('.product-price'); const originalPriceSpan = document.createElement('span'); originalPriceSpan.id = 'original-price'; originalPriceSpan.className = 'original-price'; originalPriceSpan.textContent = 'IDR ' + new Intl.NumberFormat('id-ID').format(originalPrice); priceContainer.appendChild(originalPriceSpan); } } const resellerStockElement = document.getElementById('reseller-stock'); function updateResellerStock() { if (!isNaN(selectedStock) && resellerStockElement) { resellerStockElement.textContent = stock; } } updateResellerStock() }); }); // Rating const starContainer = document.querySelector('.add-rating-stars'); const stars = document.querySelectorAll('.add-rating-star'); const ratingInput = document.getElementById('rating'); starContainer.addEventListener('mouseover', function(e) { if (e.target.classList.contains('add-rating-star')) { const rating = e.target.dataset.rating; highlightStars(rating); } }); starContainer.addEventListener('mouseout', function() { // Hanya menampilkan rating yang dipilih atau menghapus semua jika tidak ada yang dipilih const currentRating = ratingInput.value; if (currentRating) { highlightStars(currentRating); } else { clearStars(); } }); starContainer.addEventListener('click', function(e) { if (e.target.classList.contains('add-rating-star')) { const rating = e.target.dataset.rating; ratingInput.value = rating; highlightStars(rating); stars.forEach(star => { if (star.dataset.rating <= rating) { star.classList.add('selected'); } else { star.classList.remove('selected'); } }); } }); function highlightStars(rating) { stars.forEach(star => { // Mengubah bintang kosong menjadi penuh saat hover star.innerHTML = star.dataset.rating <= rating ? '★' : '☆'; }); } function clearStars() { stars.forEach(star => { // Jika bintang tidak dipilih, tampilkan bintang kosong if (!star.classList.contains('selected')) { star.innerHTML = '☆'; } else { star.innerHTML = '★'; } }); } document.getElementById('shareBtn').addEventListener('click', function() { let productTitle = "<?= addslashes($product->title) ?>"; let currentURL = window.location.href; let language = "<?= $this->session->userdata('site_lang') ?>"; // Tambahkan UTM parameters let utmURL = new URL(currentURL); utmURL.searchParams.set("utm_source", "organic_share"); utmURL.searchParams.set("utm_medium", "social"); utmURL.searchParams.set("utm_campaign", "product_share"); // Teks yang akan dibagikan let shareText = ''; let clipboardMessage = ''; if (language === 'english') { shareText = `Life's messy, but your crib don't have to be! 😏 "${productTitle}" is here to bring the fun—grab yours now!👇 ${utmURL.toString()}`; clipboardMessage = 'Text copied to clipboard! You can paste it anywhere to share.'; } else { shareText = `Hidup udah banyak drama, urusan pelengkap ranjang kudu bikin bahagia!🤩 Habek "${productTitle}" sekarang, jangan ampe keabisan!👇 ${utmURL.toString()}`; clipboardMessage = 'Teks sudah dicopy ke clipboard! Silakan paste dimana saja untuk membagikan.'; } // Cek apakah menggunakan perangkat mobile const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); if (isMobile && navigator.share) { // Untuk mobile: gunakan Web Share API navigator.share({ title: productTitle, text: shareText, url: utmURL.toString() }) .then(() => console.log('Share successful')) .catch(error => console.log('Error sharing', error)); } else { // Untuk desktop: copy ke clipboard try { navigator.clipboard.writeText(shareText) .then(() => { notyf.success(clipboardMessage); }); } catch (err) { // Fallback jika clipboard API tidak didukung const textarea = document.createElement('textarea'); textarea.value = shareText; document.body.appendChild(textarea); textarea.select(); document.execCommand('copy'); document.body.removeChild(textarea); notyf.success(clipboardMessage); } } }); }); document.addEventListener("DOMContentLoaded", () => { const mainImageContainer = document.querySelector(".main-image-container"); const mainImage = document.getElementById("main-image"); const thumbnails = document.querySelectorAll(".thumbnail"); // Popup Elements const popupOverlay = document.createElement("div"); popupOverlay.classList.add("popup-image-overlay"); const popupImage = document.createElement("img"); popupImage.classList.add("popup-image"); const leftArrow = document.createElement("button"); leftArrow.classList.add("arrow", "left-arrow"); leftArrow.innerHTML = `<i data-feather='chevron-left'></i>`; const rightArrow = document.createElement("button"); rightArrow.classList.add("arrow", "right-arrow"); rightArrow.innerHTML = `<i data-feather='chevron-right'></i>`; popupOverlay.appendChild(leftArrow); popupOverlay.appendChild(popupImage); popupOverlay.appendChild(rightArrow); document.body.appendChild(popupOverlay); let currentIndex = 0; const images = [...thumbnails].map(thumbnail => ({ src: thumbnail.src, id: thumbnail.dataset.detailId })); // Zoom follow cursor mainImageContainer.addEventListener("mousemove", (event) => { const rect = mainImageContainer.getBoundingClientRect(); const x = ((event.clientX - rect.left) / rect.width) * 100; const y = ((event.clientY - rect.top) / rect.height) * 100; mainImage.style.transformOrigin = `${x}% ${y}%`; mainImage.classList.add("zoom"); }); mainImageContainer.addEventListener("mouseleave", () => { mainImage.style.transformOrigin = "center center"; mainImage.classList.remove("zoom"); }); // Show popup mainImageContainer.addEventListener("click", () => { popupImage.src = mainImage.src; currentIndex = images.findIndex(image => image.src === mainImage.src); popupOverlay.classList.add("active"); feather.replace(); }); // Close popup popupOverlay.addEventListener("click", (event) => { if (event.target === popupOverlay || event.target === popupImage) { popupOverlay.classList.remove("active"); } }); // Navigate left leftArrow.addEventListener("click", () => { currentIndex = (currentIndex - 1 + images.length) % images.length; popupImage.src = images[currentIndex].src; }); // Navigate right rightArrow.addEventListener("click", () => { currentIndex = (currentIndex + 1) % images.length; popupImage.src = images[currentIndex].src; }); // Thumbnail click to change main image thumbnails.forEach((thumbnail, index) => { thumbnail.addEventListener("click", (event) => { const target = event.target; thumbnails.forEach((thumb) => thumb.classList.remove("active")); target.classList.add("active"); mainImage.src = target.src; currentIndex = index; // Update index for popup }); }); let touchStartX = 0; let touchEndX = 0; mainImageContainer.addEventListener("touchstart", (event) => { touchStartX = event.changedTouches[0].screenX; }); mainImageContainer.addEventListener("touchend", (event) => { touchEndX = event.changedTouches[0].screenX; handleSwipe(); }); function handleSwipe() { if (touchEndX < touchStartX - 50) { // Swipe left currentIndex = (currentIndex + 1) % images.length; mainImage.src = images[currentIndex].src; } else if (touchEndX > touchStartX + 50) { // Swipe right currentIndex = (currentIndex - 1 + images.length) % images.length; mainImage.src = images[currentIndex].src; } } }); $(document).ready(function() { // Add to cart $('#add-to-cart-button').on('click', function() { if (!selectedVariant) { notyf.error('Silakan pilih varian terlebih dahulu!'); return; } const csrfName = '<?= $this->security->get_csrf_token_name(); ?>'; const csrfHash = '<?= $this->security->get_csrf_hash(); ?>'; const quantityInput = $('#quantity'); const currentQuantity = parseInt(quantityInput.val()) || 1; const productAttribute = $(this).data('product-attribute'); const attributeDetail = $(this).data('attribute-detail'); const productVariant = `${productAttribute}: ${attributeDetail}`; const formData = { product_id: selectedProductId, product_detail_id: selectedDetailId, product_variant: selectedVariant, product_name: selectedProductName, product_image: selectedVariantImage[0]?.image || '', qty: currentQuantity, price: selectedVariantPrice, sku: selectedSKU, attribute_detail_id: selectedAttributeDetailId, [csrfName]: csrfHash }; $.ajax({ url: '<?= base_url("cart/add_to_cart") ?>', type: 'POST', dataType: 'json', data: formData, success: function(response) { if (response.status === 'success') { $('.cart-count').text(response.cart_count); $('#cart-title-count').text(response.cart_count); showLatestCartItem(response.latest_item); openCartSidebar(); } else if (response.message === 'Redirect to login') { window.location.href = "<?= base_url('login') ?>"; } else { notyf.error(response.message); } }, error: function(xhr, status, error) { notyf.error(error.message); notyf.error('Terjadi kesalahan saat menambahkan produk, hubungi contact support jika kamu menemukan notifikasi ini.'); } }); }); const cartSidebar = document.getElementById('cart-sidebar'); const continueShoppingButton = document.getElementById('continue-shopping'); const checkoutButton = document.getElementById('go-to-checkout'); const cartSidebarOverlay = document.createElement('div'); cartSidebarOverlay.classList.add('cart-sidebar-overlay'); document.body.appendChild(cartSidebarOverlay); const closeSidebarBtn = document.getElementById('close-sidebar'); closeSidebarBtn.addEventListener('click', () => { cartSidebar.classList.remove('open'); cartSidebarOverlay.classList.remove('open'); }); continueShoppingButton.addEventListener('click', () => { cartSidebar.classList.remove('open'); cartSidebarOverlay.classList.remove('open'); }); checkoutButton.addEventListener('click', () => { window.location.href = '<?= base_url("shipping") ?>'; }); cartSidebarOverlay.addEventListener('click', () => { cartSidebar.classList.remove('open'); cartSidebarOverlay.classList.remove('open'); }); // Function to open sidebar (you can call this when adding items to cart) function openCartSidebar() { cartSidebar.classList.add('open'); cartSidebarOverlay.classList.add('open'); } function showLatestCartItem(item) { const baseUrl = "<?= base_url('uploads/product/') ?>"; let itemHtml = ` <li style="display: flex; align-items: center; gap: 8px;"> <img src="${baseUrl}${item.options.image}" alt="${item.name}" style="width: 50px; height: 50px; object-fit: cover; border-radius: 4px;"> <div style="flex-grow: 1;"> <span style="font-weight: 500">${item.name}</span> <div style="margin-top: 4px;">${item.variant}</div> <div style="margin-top: 4px;">${item.qty} x IDR ${item.price.toLocaleString()}</div> </div> </li> `; $('#cart-items-sidebar').html(itemHtml); loadSuggestedProducts(item.product_id); } function loadSuggestedProducts(productId) { $.ajax({ url: `<?= base_url('cart/get_suggested_products/') ?>${productId}`, method: 'GET', success: function(response) { if (response.status === 'success' && response.data.length > 0) { const suggestedProductsContainer = $('#suggested-sidebar-products'); suggestedProductsContainer.empty(); response.data.forEach(product => { const productCard = ` <div class="product-card"> <a href="<?= base_url('product/') ?>${product.alias}" class="product-link"> <div class="product-image-container"> <img src="<?= base_url('uploads/product/') ?>${product.image}" alt="${product.title}" class="product-image first_image"> <img src="<?= base_url('uploads/product/') ?>${product.image_secondary}" alt="${product.title}" class="product-image secondary-image"> </div> <div class="product-info"> <h3 class="product-title">${product.title}</h3> </div> </a> </div> `; suggestedProductsContainer.append(productCard); }); } else { $('.cart-sidebar-suggestions').hide(); } }, error: function() { $('.cart-sidebar-suggestions').hide(); } }); } }); $(document).ready(function() { $('#wishlistBtn').on('click', function(e) { e.preventDefault(); let $button = $(this); let $icon = $button.find('i'); let isWishlisted = $button.attr('data-is-wishlisted') === 'true'; let csrfName = "<?= $this->security->get_csrf_token_name(); ?>"; let csrfHash = "<?= $this->security->get_csrf_hash(); ?>"; let formData = { product_id: <?= $initial_product_detail->product_id ?>, product_detail_id: <?= $initial_product_detail->id ?>, variant: '<?= $initial_variant['variants'] ?>', [csrfName]: csrfHash }; // Disable tombol dan tambahkan efek loading $button.prop('disabled', true); $icon.removeClass('active').addClass('loading'); $.ajax({ url: "<?= base_url('account/add_to_wishlist') ?>", type: "POST", dataType: "json", data: formData, success: function(response) { if (response.status === 'success') { // Update status wishlist $button.attr('data-is-wishlisted', response.is_wishlisted); // Update ikon if (response.is_wishlisted) { $icon.removeClass('far fa-heart outline-heart') .addClass('fas fa-heart filled-heart'); } else { $icon.removeClass('fas fa-heart filled-heart') .addClass('far fa-heart outline-heart'); } // Notifikasi notyf.success(response.message); } else { window.location.href = "<?= base_url('login') ?>"; } }, error: function(xhr) { if (xhr.status === 401) { window.location.href = "<?= base_url('login') ?>"; } else { window.location.href = "<?= base_url('login') ?>"; } }, complete: function() { // Enable tombol kembali setelah request selesai $button.prop('disabled', false); $icon.removeClass('loading').addClass('active'); } }); }); }); document.addEventListener("DOMContentLoaded", function() { function movePurchaseSection() { const purchaseSection = document.querySelector(".purchase-section"); const variantSection = document.querySelector(".variant-section"); const productInfo = document.querySelector(".product-info"); if (window.innerWidth <= 768) { // Jika dalam mode mobile, pindahkan purchase-section setelah variant-section if (variantSection && purchaseSection && !variantSection.nextElementSibling?.classList.contains("purchase-section")) { variantSection.insertAdjacentElement("afterend", purchaseSection); } } else { // Jika dalam mode desktop, pindahkan kembali ke posisi awal (di luar product-info) const productSectionGrid = document.querySelector(".product-section-grid"); if (productSectionGrid && purchaseSection) { productSectionGrid.appendChild(purchaseSection); } } } function moveProductPrice() { const productPrice = document.querySelector(".product-price"); const ratingStars = document.querySelector(".product-rating-stars"); if (window.innerWidth <= 768) { // Pindahkan product-price sebelum rating-stars if (productPrice && ratingStars && ratingStars.previousElementSibling !== productPrice) { ratingStars.parentNode.insertBefore(productPrice, ratingStars); } } } // Panggil fungsi saat halaman dimuat movePurchaseSection(); moveProductPrice(); // Panggil setiap kali ukuran layar berubah window.addEventListener("resize", function() { movePurchaseSection(); moveProductPrice(); }); setTimeout(() => { document.querySelector(".fab-cart").classList.add("show"); }, 500); const overlay = document.getElementById("fab-tutorial-overlay"); const closeBtn = document.getElementById("fab-tutorial-close"); closeBtn.addEventListener("click", function() { overlay.classList.remove("show"); localStorage.setItem("fabTutorialSeen", "true"); // Tandai sudah dilihat }); // Jalankan saat halaman dimuat showFabTutorial(); // Cek ulang saat ukuran layar berubah window.addEventListener("resize", function() { if (!isMobile()) { overlay.classList.remove("show"); // Sembunyikan jika di desktop } }); function isMobile() { return window.innerWidth <= 768; // Bisa disesuaikan sesuai kebutuhan } // Jalankan tutorial hanya di mobile function showFabTutorial() { if (!localStorage.getItem("fabTutorialSeen") && isMobile()) { setTimeout(() => { overlay.classList.add("show"); }, 1000); // Delay agar pengguna sadar } } }); function displayOriginalPrice(productName) { if (!isPromoActive) return false; const productNameLower = productName.toLowerCase(); const promoProduct = promoProducts.find(product => productNameLower.includes(product.name) ); if (promoProduct) { return promoProduct.originalPrice; } return false; } function startCountdown(startDate, durationHours) { try { // Cek apakah elemen countdown ada const daysEl = document.getElementById('days'); const hoursEl = document.getElementById('hours'); const minutesEl = document.getElementById('minutes'); const secondsEl = document.getElementById('seconds'); const promoTitle = document.querySelector('.promo-title'); const promoSubtitle = document.querySelector('.promo-subtitle'); const countdownLabel = document.querySelector('.countdown-label'); const flashBadge = document.querySelector('.flash-badge'); if (!daysEl || !hoursEl || !minutesEl || !secondsEl) { return; // Jika tidak ada, hentikan fungsi } const startTime = new Date(startDate).getTime(); const endTime = startTime + durationHours * 60 * 60 * 1000; function updateCountdown() { const now = new Date().getTime(); const timeLeft = endTime - now; if (timeLeft > 0) { const days = Math.floor(timeLeft / (1000 * 60 * 60 * 24)); const hours = Math.floor((timeLeft % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const minutes = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((timeLeft % (1000 * 60)) / 1000); daysEl.textContent = days.toString().padStart(2, '0'); hoursEl.textContent = hours.toString().padStart(2, '0'); minutesEl.textContent = minutes.toString().padStart(2, '0'); secondsEl.textContent = seconds.toString().padStart(2, '0'); setTimeout(updateCountdown, 1000); } else { if (promoTitle) promoTitle.textContent = "Promo Berakhir"; if (promoSubtitle) promoSubtitle.textContent = "Sampai ketemu di promo berikutnya!"; if (countdownLabel) countdownLabel.textContent = "Yahh waktunya udah abis"; if (flashBadge) flashBadge.style.display = "none"; daysEl.textContent = "00"; hoursEl.textContent = "00"; minutesEl.textContent = "00"; secondsEl.textContent = "00"; window.isPromoActive = false; } } updateCountdown(); } catch (error) { console.error("Error di startCountdown:", error); } } function applyPromoPrice(productName, currentPrice) { // Jika promo tidak aktif, kembalikan harga saat ini if (!isPromoActive) return currentPrice; // Ubah nama produk ke lowercase untuk perbandingan yang konsisten const productNameLower = productName.toLowerCase(); // Cari produk dalam daftar promo const promoProduct = promoProducts.find(product => productNameLower.includes(product.name) ); // Jika produk ditemukan dalam daftar promo, kembalikan harga promo if (promoProduct) { return promoProduct.promoPrice; } // Jika tidak, kembalikan harga saat ini return currentPrice; } </script>