ok
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
width: 40vw;
|
||||
height: 260px;
|
||||
background-color: #e2e8f0;
|
||||
border-radius: 0.75rem;
|
||||
@@ -20,6 +20,10 @@
|
||||
color: #64748b;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.headerRow {
|
||||
@@ -32,14 +36,14 @@
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.1rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
color: #1e293b;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.price {
|
||||
font-size: 1.1rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
color: #2563eb; /* default color, bisa override di inline style */
|
||||
}
|
||||
@@ -92,4 +96,41 @@
|
||||
.buttonGroup {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 63vw;
|
||||
}
|
||||
}
|
||||
|
||||
.childSelector {
|
||||
background: white;
|
||||
color: black;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.childProduct {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
border: 1px solid black;
|
||||
padding: 10px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.confirmButton {
|
||||
background-color: #2563eb;
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
margin-right: 10px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cancelButton {
|
||||
background-color: #f87171;
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ import styles from './ProductDetail.module.css';
|
||||
|
||||
const ProductDetail = ({ product, setPostLoginAction, setShowedModal }) => {
|
||||
const [inCart, setInCart] = useState(false);
|
||||
const [showChildSelector, setShowChildSelector] = useState(false);
|
||||
const [selectedChildIds, setSelectedChildIds] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const existingCookie = document.cookie
|
||||
@@ -47,85 +49,193 @@ const ProductDetail = ({ product, setPostLoginAction, setShowedModal }) => {
|
||||
setInCart(true);
|
||||
}
|
||||
|
||||
document.cookie = `itemsId=${JSON.stringify(updatedItems)}; path=/; max-age=${7 * 24 * 60 * 60
|
||||
}`;
|
||||
document.cookie = `itemsId=${JSON.stringify(updatedItems)}; path=/; max-age=${7 * 24 * 60 * 60}`;
|
||||
};
|
||||
|
||||
const onCheckout = () => {
|
||||
// Ambil token dari cookie
|
||||
const tokenCookie = document.cookie
|
||||
.split('; ')
|
||||
.find(row => row.startsWith('token='));
|
||||
const token = tokenCookie ? tokenCookie.split('=')[1] : '';
|
||||
const onCheckout = () => {
|
||||
const tokenCookie = document.cookie
|
||||
.split('; ')
|
||||
.find(row => row.startsWith('token='));
|
||||
const token = tokenCookie ? tokenCookie.split('=')[1] : '';
|
||||
|
||||
// Ambil itemsId dari cookie
|
||||
const itemsCookie = document.cookie
|
||||
.split('; ')
|
||||
.find(row => row.startsWith('itemsId='));
|
||||
if (!tokenCookie) {
|
||||
setPostLoginAction(() => () => onCheckout());
|
||||
setShowedModal('login');
|
||||
return;
|
||||
}
|
||||
|
||||
let items = [];
|
||||
if (itemsCookie) {
|
||||
try {
|
||||
items = JSON.parse(itemsCookie.split('=')[1]);
|
||||
if (!Array.isArray(items)) items = [];
|
||||
} catch (e) {
|
||||
items = [];
|
||||
}
|
||||
// Jika punya children, tampilkan pilihan
|
||||
if (product.children && product.children.length > 0) {
|
||||
setShowChildSelector(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ambil itemsId dari cookie
|
||||
const itemsCookie = document.cookie
|
||||
.split('; ')
|
||||
.find(row => row.startsWith('itemsId='));
|
||||
|
||||
let items = [];
|
||||
if (itemsCookie) {
|
||||
try {
|
||||
items = JSON.parse(itemsCookie.split('=')[1]);
|
||||
if (!Array.isArray(items)) items = [];
|
||||
} catch (e) {
|
||||
items = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (!items.includes(product.id)) {
|
||||
items.push(product.id);
|
||||
// Tambahkan product.id jika belum ada
|
||||
if (!items.includes(product.id)) {
|
||||
items.push(product.id);
|
||||
}
|
||||
|
||||
const itemsParam = JSON.stringify(items);
|
||||
|
||||
window.location.href = `http://localhost:3002/?token=${token}&itemsId=${itemsParam}&redirect_uri=http://localhost:3000/products&redirect_failed=http://localhost:3000`;
|
||||
};
|
||||
|
||||
const onConfirmChildren = () => {
|
||||
const tokenCookie = document.cookie
|
||||
.split('; ')
|
||||
.find(row => row.startsWith('token='));
|
||||
const token = tokenCookie ? tokenCookie.split('=')[1] : '';
|
||||
|
||||
if (selectedChildIds.length === 0) {
|
||||
alert('Pilih minimal satu produk');
|
||||
return;
|
||||
}
|
||||
|
||||
// Ambil itemsId dari cookie
|
||||
const itemsCookie = document.cookie
|
||||
.split('; ')
|
||||
.find(row => row.startsWith('itemsId='));
|
||||
|
||||
let items = [];
|
||||
if (itemsCookie) {
|
||||
try {
|
||||
items = JSON.parse(itemsCookie.split('=')[1]);
|
||||
if (!Array.isArray(items)) items = [];
|
||||
} catch (e) {
|
||||
items = [];
|
||||
}
|
||||
// Encode items ke string untuk query param
|
||||
const itemsParam = JSON.stringify(items);
|
||||
}
|
||||
|
||||
if (!tokenCookie) {
|
||||
setPostLoginAction(() => () => onCheckout()); // remember intent
|
||||
setShowedModal('login');
|
||||
return;
|
||||
}
|
||||
// Gabungkan items dari cookie dengan selectedChildIds
|
||||
const mergedItems = Array.from(new Set([...items, ...selectedChildIds]));
|
||||
|
||||
// Redirect dengan token dan itemsId di query route ke checkout.kediritechnopark.com
|
||||
window.location.href = `http://localhost:3002/?token=${token}&itemsId=${itemsParam}&redirect_uri=http://localhost:3000/products&redirect_failed=http://localhost:3000`;
|
||||
};
|
||||
const itemsParam = JSON.stringify(mergedItems);
|
||||
|
||||
window.location.href = `http://localhost:3002/?token=${token}&itemsId=${itemsParam}&redirect_uri=http://localhost:3000/products&redirect_failed=http://localhost:3000`;
|
||||
};
|
||||
|
||||
|
||||
// Override harga warna jika free
|
||||
const priceColor = product.price === 0 ? '#059669' : '#2563eb';
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.image}>📦</div>
|
||||
|
||||
<div className={styles.headerRow}>
|
||||
<h2 className={styles.title}>{product.name}</h2>
|
||||
<div className={styles.price} style={{ color: priceColor }}>
|
||||
{product.price == null
|
||||
? 'Pay-As-You-Go'
|
||||
: `Rp ${parseInt(product.price).toLocaleString('id-ID')}`}
|
||||
{/* ✅ Tampilan utama disembunyikan jika sedang memilih child */}
|
||||
{!showChildSelector && (
|
||||
<>
|
||||
<div
|
||||
className={styles.image}
|
||||
style={{ backgroundImage: `url(${product.image})` }}
|
||||
></div>
|
||||
|
||||
<div className={styles.headerRow}>
|
||||
<h2 className={styles.title}>{product.name}</h2>
|
||||
<div className={styles.price} style={{ color: priceColor }}>
|
||||
{product.price == null
|
||||
? 'Pay-As-You-Go'
|
||||
: `Rp ${parseInt(product.price).toLocaleString('id-ID')}`}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className={styles.description}>{product.description}</p>
|
||||
|
||||
<div className={styles.buttonGroup}>
|
||||
<button
|
||||
className={`${styles.button} ${styles.addToCartButton}`}
|
||||
onClick={onSetCart}
|
||||
onMouseOver={e => (e.target.style.backgroundColor = '#facc15')}
|
||||
onMouseOut={e => (e.target.style.backgroundColor = '#fbbf24')}
|
||||
>
|
||||
<img
|
||||
src={'/cart-shopping-svgrepo-com.svg'}
|
||||
alt={inCart ? 'Hapus' : 'Tambah'}
|
||||
style={{ width: '21px', height: '21px', marginRight: '7px' }}
|
||||
/>
|
||||
{inCart ? 'Hapus' : 'Tambah'}
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`${styles.button} ${styles.checkoutButton}`}
|
||||
onClick={onCheckout}
|
||||
onMouseOver={e => (e.target.style.backgroundColor = '#1d4ed8')}
|
||||
onMouseOut={e => (e.target.style.backgroundColor = '#2563eb')}
|
||||
>
|
||||
Checkout
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* ✅ UI pemilihan child */}
|
||||
{showChildSelector && (
|
||||
<div className={styles.childSelector}>
|
||||
<h3>Pilih Paket</h3>
|
||||
{product.children.map(child => (
|
||||
<label key={child.id} className={styles.childProduct} style={{ display: 'block', marginBottom: '8px' }}>
|
||||
<input
|
||||
type="checkbox"
|
||||
value={child.id}
|
||||
checked={selectedChildIds.includes(child.id)}
|
||||
onChange={e => {
|
||||
const checked = e.target.checked;
|
||||
if (checked) {
|
||||
setSelectedChildIds(prev => [...prev, child.id]);
|
||||
} else {
|
||||
setSelectedChildIds(prev => prev.filter(id => id !== child.id));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{' '}
|
||||
{child.name} — Rp {parseInt(child.price || 0).toLocaleString('id-ID')}
|
||||
</label>
|
||||
))}
|
||||
|
||||
<p style={{ marginTop: '10px' }}>
|
||||
<strong>Total Harga:</strong>{' '}
|
||||
Rp {selectedChildIds
|
||||
.map(id => {
|
||||
const found = product.children.find(child => child.id === id);
|
||||
return found ? found.price || 0 : 0;
|
||||
})
|
||||
.reduce((a, b) => a + b, 0)
|
||||
.toLocaleString('id-ID')}
|
||||
</p>
|
||||
|
||||
<div className={styles.buttonGroup}>
|
||||
|
||||
<button
|
||||
className={`${styles.button} ${styles.cancelButton}`}
|
||||
onClick={() => {
|
||||
setShowChildSelector(false);
|
||||
setSelectedChildIds([]);
|
||||
}}
|
||||
>
|
||||
Kembali
|
||||
</button>
|
||||
<button
|
||||
className={`${styles.button} ${styles.confirmButton}`}
|
||||
onClick={onConfirmChildren}
|
||||
>
|
||||
Lanjut ke Checkout
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className={styles.description}>{product.description}</p>
|
||||
|
||||
<div className={styles.buttonGroup}>
|
||||
<button
|
||||
className={`${styles.button} ${styles.addToCartButton}`}
|
||||
onClick={onSetCart}
|
||||
onMouseOver={e => (e.target.style.backgroundColor = '#facc15')}
|
||||
onMouseOut={e => (e.target.style.backgroundColor = '#fbbf24')}
|
||||
>
|
||||
{inCart ? 'Hapus dari Keranjang' : '+ Keranjang'}
|
||||
</button>
|
||||
<button
|
||||
className={`${styles.button} ${styles.checkoutButton}`}
|
||||
onClick={onCheckout}
|
||||
onMouseOver={e => (e.target.style.backgroundColor = '#1d4ed8')}
|
||||
onMouseOut={e => (e.target.style.backgroundColor = '#2563eb')}
|
||||
>
|
||||
Checkout
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,18 +6,48 @@ import styles from './Styles.module.css';
|
||||
const ProductSection = ({ hoveredCard, setHoveredCard, setSelectedProduct, setShowedModal, productSectionRef }) => {
|
||||
const [products, setProducts] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('https://bot.kediritechnopark.com/webhook/store-dev/products', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ type: 'product', onlyParents: true }),
|
||||
useEffect(() => {
|
||||
fetch('https://bot.kediritechnopark.com/webhook/store-dev/products', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ type: 'product' }),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
const parentMap = {};
|
||||
const childrenMap = {};
|
||||
|
||||
// Pisahkan parent dan child
|
||||
data.forEach(product => {
|
||||
if (product.sub_product_of) {
|
||||
const parentId = product.sub_product_of;
|
||||
if (!childrenMap[parentId]) childrenMap[parentId] = [];
|
||||
childrenMap[parentId].push(product);
|
||||
} else {
|
||||
parentMap[product.id] = {
|
||||
...product,
|
||||
children: []
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Pasang children ke parent
|
||||
Object.keys(childrenMap).forEach(parentId => {
|
||||
const parent = parentMap[parentId];
|
||||
if (parent) {
|
||||
parent.children = childrenMap[parentId];
|
||||
}
|
||||
});
|
||||
|
||||
// Ambil parent saja
|
||||
const enrichedData = Object.values(parentMap);
|
||||
|
||||
setProducts(enrichedData);
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => setProducts(data))
|
||||
.catch(err => console.error('Fetch error:', err));
|
||||
}, []);
|
||||
.catch(err => console.error('Fetch error:', err));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -31,7 +61,8 @@ const ProductSection = ({ hoveredCard, setHoveredCard, setSelectedProduct, setSh
|
||||
<div className={styles.coursesGrid}>
|
||||
{products &&
|
||||
products[0]?.name &&
|
||||
products.map(product => (
|
||||
products
|
||||
.map(product => (
|
||||
<div
|
||||
key={product.id}
|
||||
className={`${styles.courseCard} ${hoveredCard === product.id ? styles.courseCardHover : ''}`}
|
||||
|
||||
@@ -3,7 +3,7 @@ import ProductDetailPage from '../ProductDetailPage';
|
||||
import Login from '../Login';
|
||||
import styles from '../Styles.module.css';
|
||||
|
||||
// Fungsi simple untuk parsing token JWT dan mengembalikan payload JSON
|
||||
|
||||
function parseJwt(token) {
|
||||
try {
|
||||
const base64Url = token.split('.')[1];
|
||||
@@ -20,32 +20,122 @@ function parseJwt(token) {
|
||||
}
|
||||
}
|
||||
|
||||
function getDistinctProductIdsFromJwt(token) {
|
||||
const payload = parseJwt(token);
|
||||
if (!payload || !payload.subscriptions || !payload.subscriptions) return [];
|
||||
|
||||
const productIds = payload.subscriptions.map(p => p.product_id);
|
||||
return [...new Set(productIds)];
|
||||
}
|
||||
|
||||
function getLatestEndDatesFromJwt(token) {
|
||||
const payload = parseJwt(token);
|
||||
if (!payload || !payload.subscriptions || !payload.subscriptions) return {};
|
||||
|
||||
const result = {};
|
||||
payload.subscriptions.forEach(p => {
|
||||
if (!p.end_date) return;
|
||||
const id = p.product_id;
|
||||
const endDate = new Date(p.end_date);
|
||||
if (!result[id] || endDate > new Date(result[id])) {
|
||||
result[id] = p.end_date;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
function getTotalTokenFromJwt(token) {
|
||||
const payload = parseJwt(token);
|
||||
if (!payload || !payload.subscriptions || !payload.subscriptions) return {};
|
||||
|
||||
const tokenQuantities = {};
|
||||
payload.subscriptions.forEach(p => {
|
||||
// Pastikan ada quantity dan unit_type token
|
||||
if (p.quantity && p.product_id) {
|
||||
tokenQuantities[p.product_id] = (tokenQuantities[p.product_id] || 0) + p.quantity;
|
||||
}
|
||||
});
|
||||
|
||||
return tokenQuantities;
|
||||
}
|
||||
|
||||
|
||||
const CoursePage = () => {
|
||||
const [postLoginAction, setPostLoginAction] = useState(null);
|
||||
const [selectedProduct, setSelectedProduct] = useState({});
|
||||
const [hoveredCard, setHoveredCard] = useState(null);
|
||||
const [showedModal, setShowedModal] = useState(null); // 'product' | 'login' | null
|
||||
const [showedModal, setShowedModal] = useState(null);
|
||||
const [products, setProducts] = useState([]);
|
||||
useEffect(() => {
|
||||
const match = document.cookie.match(new RegExp('(^| )token=([^;]+)'));
|
||||
if (match) {
|
||||
const token = match[2];
|
||||
|
||||
useEffect(() => {
|
||||
// Ambil token dari cookies
|
||||
const match = document.cookie.match(new RegExp('(^| )token=([^;]+)'));
|
||||
if (match) {
|
||||
const token = match[2];
|
||||
const productIds = getDistinctProductIdsFromJwt(token);
|
||||
const endDates = getLatestEndDatesFromJwt(token);
|
||||
const tokenQuantitiesFromJwt = getTotalTokenFromJwt(token);
|
||||
|
||||
fetch('https://bot.kediritechnopark.com/webhook/users-dev/my-products', {
|
||||
fetch('https://bot.kediritechnopark.com/webhook/store-dev/products', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + token
|
||||
},
|
||||
body: JSON.stringify({ type: 'product' }),
|
||||
body: JSON.stringify({ itemsId: productIds, type: 'product' }),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => setProducts(data))
|
||||
.catch(err => console.error('Fetch error:', err));
|
||||
.then(data => {
|
||||
const parentMap = {};
|
||||
const childrenMap = {};
|
||||
|
||||
data.forEach(product => {
|
||||
if (product.sub_product_of) {
|
||||
const parentId = product.sub_product_of;
|
||||
if (!childrenMap[parentId]) childrenMap[parentId] = [];
|
||||
childrenMap[parentId].push(product);
|
||||
} else {
|
||||
parentMap[product.id] = {
|
||||
...product,
|
||||
quantity: product.quantity || 0,
|
||||
end_date: endDates[product.id] || null,
|
||||
children: []
|
||||
};
|
||||
}
|
||||
});
|
||||
// ...
|
||||
|
||||
Object.keys(childrenMap).forEach(parentId => {
|
||||
const parent = parentMap[parentId];
|
||||
const children = childrenMap[parentId];
|
||||
|
||||
if (parent) {
|
||||
parent.children = children;
|
||||
|
||||
// Pakai quantity dari JWT langsung (tokenQuantitiesFromJwt)
|
||||
parent.quantity = children.reduce((total, child) => {
|
||||
return total + (tokenQuantitiesFromJwt[child.id] || 0);
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
|
||||
// ...
|
||||
|
||||
// Update quantity untuk produk yang bukan parent dan bukan anak
|
||||
Object.values(parentMap).forEach(product => {
|
||||
if (!product.children.length) {
|
||||
if (product.unit_type === 'token') {
|
||||
product.quantity = tokenQuantitiesFromJwt[product.id] || 0;
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
});
|
||||
|
||||
const enrichedData = Object.values(parentMap);
|
||||
setProducts(enrichedData);
|
||||
console.log(enrichedData);
|
||||
})
|
||||
.catch(err => console.error('Fetch error:', err));
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
const features = [
|
||||
{
|
||||
@@ -70,49 +160,52 @@ const CoursePage = () => {
|
||||
|
||||
return (
|
||||
<div style={{ fontFamily: 'Inter, system-ui, sans-serif' }}>
|
||||
|
||||
|
||||
{/* Courses Section */}
|
||||
<section className={styles.Section}>
|
||||
<div className={styles.coursesContainer}>
|
||||
<h2 className={styles.coursesTitle}>OUR COURSES</h2>
|
||||
<h2 className={styles.coursesTitle}>MY PRODUCTS</h2>
|
||||
<div className={styles.coursesGrid}>
|
||||
{products &&
|
||||
products[0]?.name &&
|
||||
products.map(product => (
|
||||
<div
|
||||
key={product.id}
|
||||
className={`${styles.courseCard} ${hoveredCard === product.id ? styles.courseCardHover : ''}`}
|
||||
onClick={() => {
|
||||
setSelectedProduct(product);
|
||||
setShowedModal('product');
|
||||
}}
|
||||
onMouseEnter={() => setHoveredCard(product.id)}
|
||||
onMouseLeave={() => setHoveredCard(null)}
|
||||
>
|
||||
<div className={styles.courseImage}>
|
||||
{product.price == 0 && (
|
||||
<span className={styles.courseLabel}>Free</span>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.courseContent}>
|
||||
<h3 className={styles.courseTitle}>{product.name}</h3>
|
||||
<p className={styles.courseDesc}>{product.description}</p>
|
||||
<div className={styles.coursePrice}>
|
||||
<span
|
||||
className={
|
||||
product.price == 0
|
||||
? styles.freePrice
|
||||
: styles.currentPrice
|
||||
}
|
||||
>
|
||||
{product.price == 0
|
||||
? 'Free'
|
||||
: `Rp ${product.price.toLocaleString('id-ID')}`}
|
||||
</span>
|
||||
products
|
||||
.map(product => (
|
||||
<div
|
||||
key={product.id}
|
||||
className={`${styles.courseCard} ${hoveredCard === product.id ? styles.courseCardHover : ''}`}
|
||||
onClick={() => {
|
||||
setSelectedProduct(product);
|
||||
setShowedModal('product');
|
||||
}}
|
||||
onMouseEnter={() => setHoveredCard(product.id)}
|
||||
onMouseLeave={() => setHoveredCard(null)}
|
||||
>
|
||||
<div className={styles.courseImage} style={{ backgroundImage: `url(${product.image})` }}>
|
||||
{product.price == 0 && (
|
||||
<span className={styles.courseLabel}>Free</span>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.courseContent}>
|
||||
<h3 className={styles.courseTitle}>{product.name}</h3>
|
||||
<p className={styles.courseDesc}>{product.description}</p>
|
||||
<div className={styles.coursePrice}>
|
||||
<span
|
||||
className={
|
||||
product.price == 0
|
||||
? styles.freePrice
|
||||
: styles.currentPrice
|
||||
}
|
||||
>
|
||||
{product.unit_type === 'duration'
|
||||
? `Valid until: ${product.end_date ? new Date(product.end_date).toLocaleDateString() : 'N/A'}`
|
||||
: `SISA TOKEN ${product.quantity || 0}`
|
||||
}
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user