|
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 : /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>
<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-original-price="<?= $detail->original_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['formatted_variants'] ?>"
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['formatted_variants']); ?>">
<?php if ($variant['product_attribute'] !== 'Warna'): ?>
<?= htmlspecialchars($variant['formatted_variants']); ?>
<?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>
<script>
let selectedProductId = null;
let selectedVariant = null;
let selectedDetailId = null;
let selectedStock = null;
let selectedVariantPrice = null;
let selectedVariantOriginalPrice = null;
let selectedProductName = null;
let selectedVariantImage = null;
let selectedSKU = null;
let selectedAttributeDetailId = null;
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,
originalPrice: <?= $initial_product_detail->original_price ?>,
sku: "<?= $initial_product_detail->sku ?>",
attributeDetailId: "<?= $initial_variant->attribute_detail_id ?>",
variant: {
attribute: "<?= $initial_variant['product_attribute'] ?>",
detail: "<?= $initial_variant['attribute_detail'] ?>",
formatted_variants: "<?= $initial_variant['formatted_variants'] ?>",
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.formatted_variants;
selectedStock = initialDetail.stock;
selectedVariantPrice = initialDetail.price;
selectedVariantOriginalPrice = initialDetail.originalPrice;
selectedProductName = initialDetail.productName;
selectedVariantImage = initialDetail.images;
selectedSKU = initialDetail.sku;
selectedAttributeDetailId = initialDetail.attributeDetailId
console.log(initialDetail.price);
console.log(initialDetail.originalPrice);
const priceElement = document.getElementById('price');
if (priceElement) {
priceElement.textContent = 'IDR ' + new Intl.NumberFormat('id-ID').format(selectedVariantPrice);
}
const originalPrice = selectedVariantOriginalPrice;
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);
}
}
}
});
// 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);
selectedVariantOriginalPrice = parseFloat(this.dataset.originalPrice);
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 = selectedVariantOriginalPrice;
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
}
}
});
</script>