Compare commits
3 Commits
8d677eda1d
...
e3154e4cde
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3154e4cde | ||
|
|
7add8f2864 | ||
|
|
361ba6316b |
32
src/App.js
32
src/App.js
@@ -10,9 +10,9 @@ import ProductSection from './components/ProductSection';
|
|||||||
import AcademySection from './components/AcademySection';
|
import AcademySection from './components/AcademySection';
|
||||||
import AboutUsSection from './components/AboutUsSection';
|
import AboutUsSection from './components/AboutUsSection';
|
||||||
// KnowledgeBaseSection hidden temporarily
|
// KnowledgeBaseSection hidden temporarily
|
||||||
// import KnowledgeBaseSection from './components/KnowledgeBaseSection';
|
import KnowledgeBaseSection from './components/KnowledgeBaseSection';
|
||||||
// ClientsSection hidden temporarily
|
// ClientsSection hidden temporarily
|
||||||
// import ClientsSection from './components/ClientsSection';
|
import ClientsSection from './components/ClientsSection';
|
||||||
import FAQSection from './components/FAQSection';
|
import FAQSection from './components/FAQSection';
|
||||||
import Footer from './components/Footer';
|
import Footer from './components/Footer';
|
||||||
import ProductDetailPage from './components/ProductDetailPage';
|
import ProductDetailPage from './components/ProductDetailPage';
|
||||||
@@ -27,8 +27,19 @@ function HomePage({
|
|||||||
setShowedModal,
|
setShowedModal,
|
||||||
productSectionRef,
|
productSectionRef,
|
||||||
courseSectionRef,
|
courseSectionRef,
|
||||||
|
scrollToProduct,
|
||||||
|
scrollToCourse,
|
||||||
setWillDo
|
setWillDo
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const tab = params.get('tab');
|
||||||
|
|
||||||
|
if (tab === 'products') scrollToProduct();
|
||||||
|
if (tab === 'academy') scrollToCourse();
|
||||||
|
}, [productSectionRef, courseSectionRef]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HeroSection />
|
<HeroSection />
|
||||||
@@ -47,9 +58,9 @@ function HomePage({
|
|||||||
setShowedModal={setShowedModal}
|
setShowedModal={setShowedModal}
|
||||||
setWillDo={setWillDo}
|
setWillDo={setWillDo}
|
||||||
/>
|
/>
|
||||||
{/* <KnowledgeBaseSection /> */}
|
<KnowledgeBaseSection />
|
||||||
{/* <ClientsSection /> */}
|
<ClientsSection />
|
||||||
<FAQSection />
|
<Footer />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -134,6 +145,7 @@ function App() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
const modalType = params.get('modal');
|
const modalType = params.get('modal');
|
||||||
|
const tab = params.get('tab');
|
||||||
const productId = params.get('product_id');
|
const productId = params.get('product_id');
|
||||||
const authorizedUri = params.get('authorized_uri');
|
const authorizedUri = params.get('authorized_uri');
|
||||||
const unauthorizedUri = params.get('unauthorized_uri');
|
const unauthorizedUri = params.get('unauthorized_uri');
|
||||||
@@ -152,6 +164,9 @@ function App() {
|
|||||||
}
|
}
|
||||||
// Jika sudah login, tidak langsung fetch di sini — akan diproses saat subscriptions tersedia
|
// Jika sudah login, tidak langsung fetch di sini — akan diproses saat subscriptions tersedia
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tab === 'products') scrollToProduct();
|
||||||
|
if (tab === 'academy') scrollToCourse();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -303,12 +318,14 @@ function App() {
|
|||||||
productSectionRef={productSectionRef}
|
productSectionRef={productSectionRef}
|
||||||
courseSectionRef={courseSectionRef}
|
courseSectionRef={courseSectionRef}
|
||||||
setWillDo={setWillDo}
|
setWillDo={setWillDo}
|
||||||
|
scrollToProduct={scrollToProduct}
|
||||||
|
scrollToCourse={scrollToCourse}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route path="/dashboard" element={<ProductsPage
|
<Route path="/dashboard" element={<ProductsPage
|
||||||
setShowedModal={setShowedModal}
|
setShowedModal={setShowedModal}
|
||||||
setSelectedProduct={setSelectedProduct} subscriptions={subscriptions} />} />
|
setSelectedProduct={setSelectedProduct} subscriptions={subscriptions} setWillDo={setWillDo} />} />
|
||||||
<Route
|
<Route
|
||||||
path="/admin"
|
path="/admin"
|
||||||
element={
|
element={
|
||||||
@@ -321,7 +338,6 @@ function App() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
<Footer />
|
|
||||||
|
|
||||||
{/* Modal */}
|
{/* Modal */}
|
||||||
{showedModal && (
|
{showedModal && (
|
||||||
|
|||||||
@@ -105,6 +105,10 @@ const LoginRegister = ({setShowedModal}) => {
|
|||||||
window.history.replaceState({}, '', newUrl);
|
window.history.replaceState({}, '', newUrl);
|
||||||
setShowedModal('product');
|
setShowedModal('product');
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const modalType = params.get('modal');
|
||||||
|
if(!modalType)
|
||||||
navigate('/dashboard');
|
navigate('/dashboard');
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import styles from './ProductDetail.module.css';
|
import styles from './ProductDetail.module.css';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin, setShowedModal }) => {
|
const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin, setShowedModal }) => {
|
||||||
const [showChildSelector, setShowChildSelector] = useState(false);
|
const [showChildSelector, setShowChildSelector] = useState(false);
|
||||||
const [selectedChildIds, setSelectedChildIds] = useState([]);
|
const [selectedChildIds, setSelectedChildIds] = useState([]);
|
||||||
|
|
||||||
const [matchingSubscriptions, setMatchingSubscriptions] = useState([]);
|
const [matchingSubscriptions, setMatchingSubscriptions] = useState([]);
|
||||||
const [selectedSubscriptionId, setSelectedSubscriptionId] = useState(null);
|
const [selectedSubscriptionId, setSelectedSubscriptionId] = useState(0);
|
||||||
const [showSubscriptionSelector, setShowSubscriptionSelector] = useState(false);
|
const [showSubscriptionSelector, setShowSubscriptionSelector] = useState(false);
|
||||||
|
|
||||||
const [showNamingInput, setShowNamingInput] = useState(false);
|
const [showNamingInput, setShowNamingInput] = useState(false);
|
||||||
const [customName, setCustomName] = useState('');
|
const [customName, setCustomName] = useState('');
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const onCheckout = () => {
|
const onCheckout = () => {
|
||||||
const tokenCookie = document.cookie.split('; ').find(row => row.startsWith('token='));
|
const tokenCookie = document.cookie.split('; ').find(row => row.startsWith('token='));
|
||||||
const token = tokenCookie ? tokenCookie.split('=')[1] : '';
|
const token = tokenCookie ? tokenCookie.split('=')[1] : '';
|
||||||
@@ -20,34 +23,14 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
|||||||
requestLogin('checkout');
|
requestLogin('checkout');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (product.type == 'product') {
|
|
||||||
|
if (product.type === 'product') {
|
||||||
const hasMatchingSubscription = Array.isArray(subscriptions) &&
|
const hasMatchingSubscription = Array.isArray(subscriptions) &&
|
||||||
subscriptions.some(sub =>
|
subscriptions.some(sub =>
|
||||||
String(sub.product_id) === String(product.id) || String(sub.product_parent_id) === String(product.id)
|
String(sub.product_id) === String(product.id) || String(sub.product_parent_id) === String(product.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Always show children selector first if product has children
|
// ✅ Check subscription first
|
||||||
if (product.children && product.children.length > 0) {
|
|
||||||
setShowChildSelector(true);
|
|
||||||
|
|
||||||
if (hasMatchingSubscription) {
|
|
||||||
const matching = subscriptions.filter(sub =>
|
|
||||||
String(sub.product_id) === String(product.id) || String(sub.product_parent_id) === String(product.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (matching.length > 0) {
|
|
||||||
// ✅ Select only the first for each product_name
|
|
||||||
const uniqueByName = Array.from(
|
|
||||||
new Map(matching.map(sub => [sub.product_name, sub])).values()
|
|
||||||
);
|
|
||||||
|
|
||||||
setMatchingSubscriptions(uniqueByName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No children, but has subscription match
|
|
||||||
if (hasMatchingSubscription) {
|
if (hasMatchingSubscription) {
|
||||||
const matching = subscriptions.filter(sub =>
|
const matching = subscriptions.filter(sub =>
|
||||||
String(sub.product_id) === String(product.id) || String(sub.product_parent_id) === String(product.id)
|
String(sub.product_id) === String(product.id) || String(sub.product_parent_id) === String(product.id)
|
||||||
@@ -61,56 +44,59 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
|||||||
setMatchingSubscriptions(uniqueByName);
|
setMatchingSubscriptions(uniqueByName);
|
||||||
setShowSubscriptionSelector(true);
|
setShowSubscriptionSelector(true);
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
const itemsParam = JSON.stringify([product.id]);
|
const itemsParam = JSON.stringify([product.id]);
|
||||||
window.location.href = `https://checkout.kediritechnopark.com/?token=${token}&itemsId=${itemsParam}&set_name=${product.name}&redirect_uri=https://kediritechnopark.com/products&redirect_failed=https://kediritechnopark.com`;
|
window.location.href = `https://checkout.kediritechnopark.com/?token=${token}&itemsId=${itemsParam}&set_name=${product.name}&redirect_uri=https://kediritechnopark.com/products&redirect_failed=https://kediritechnopark.com`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ If no subscription → ask for new product name
|
||||||
setShowNamingInput(true);
|
setShowNamingInput(true);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
// No children, no matching subscription
|
|
||||||
|
// Fallback: direct checkout
|
||||||
const itemsParam = JSON.stringify([product.id]);
|
const itemsParam = JSON.stringify([product.id]);
|
||||||
window.location.href = `https://checkout.kediritechnopark.com/?token=${token}&itemsId=${itemsParam}&redirect_uri=https://kediritechnopark.com/products&redirect_failed=https://kediritechnopark.com`;
|
window.location.href = `https://checkout.kediritechnopark.com/?token=${token}&itemsId=${itemsParam}&redirect_uri=https://kediritechnopark.com/products&redirect_failed=https://kediritechnopark.com`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ✅ Confirm child selection (final step after naming)
|
||||||
const onConfirmChildren = () => {
|
const onConfirmChildren = () => {
|
||||||
if (matchingSubscriptions.length > 0) {
|
if (selectedChildIds.length === 0) {
|
||||||
setShowChildSelector(false);
|
alert('Pilih minimal satu produk');
|
||||||
setShowSubscriptionSelector(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setShowChildSelector(false);
|
|
||||||
setShowNamingInput(true);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokenCookie = document.cookie.split('; ').find(row => row.startsWith('token='));
|
const tokenCookie = document.cookie.split('; ').find(row => row.startsWith('token='));
|
||||||
const token = tokenCookie ? tokenCookie.split('=')[1] : '';
|
const token = tokenCookie ? tokenCookie.split('=')[1] : '';
|
||||||
|
|
||||||
if (selectedChildIds.length === 0) {
|
const encodedName = encodeURIComponent(customName.trim() || product.name);
|
||||||
alert('Pilih minimal satu produk');
|
const itemsParam = JSON.stringify(selectedChildIds);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemsParam = selectedChildIds.length > 0 ? JSON.stringify(selectedChildIds) : JSON.stringify([product.id]);
|
window.location.href = `https://checkout.kediritechnopark.com/?token=${token}&itemsId=${itemsParam}&new_name=${encodedName}&redirect_uri=https://kediritechnopark.com/products&redirect_failed=https://kediritechnopark.com`;
|
||||||
window.location.href = `https://checkout.kediritechnopark.com/?token=${token}&itemsId=${itemsParam}&redirect_uri=https://kediritechnopark.com/products&redirect_failed=https://kediritechnopark.com`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ✅ User sets name first → then if product has children, show child selector
|
||||||
const onFinalCheckoutNewProduct = () => {
|
const onFinalCheckoutNewProduct = () => {
|
||||||
if (!customName.trim()) {
|
if (!customName.trim()) {
|
||||||
alert('Nama produk tidak boleh kosong');
|
alert('Nama produk tidak boleh kosong');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (product.children && product.children.length > 0) {
|
||||||
|
// don’t redirect yet → go to child selector
|
||||||
|
setShowSubscriptionSelector(false);
|
||||||
|
|
||||||
|
setShowNamingInput(false);
|
||||||
|
setShowChildSelector(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no children → go straight to checkout
|
||||||
const tokenCookie = document.cookie.split('; ').find(row => row.startsWith('token='));
|
const tokenCookie = document.cookie.split('; ').find(row => row.startsWith('token='));
|
||||||
const token = tokenCookie ? tokenCookie.split('=')[1] : '';
|
const token = tokenCookie ? tokenCookie.split('=')[1] : '';
|
||||||
const itemsParam = selectedChildIds.length > 0 ? JSON.stringify(selectedChildIds) : JSON.stringify([product.id]);
|
const itemsParam = JSON.stringify([product.id]);
|
||||||
const encodedName = encodeURIComponent(customName.trim());
|
const encodedName = encodeURIComponent(customName.trim());
|
||||||
|
|
||||||
window.location.href = `https://checkout.kediritechnopark.com/?token=${token}&itemsId=${itemsParam}&new_name=${encodedName}&redirect_uri=https://kediritechnopark.com/products&redirect_failed=https://kediritechnopark.com`;
|
window.location.href = `https://checkout.kediritechnopark.com/?token=${token}&itemsId=${itemsParam}&new_name=${encodedName}&redirect_uri=https://kediritechnopark.com/products&redirect_failed=https://kediritechnopark.com`;
|
||||||
@@ -143,13 +129,14 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
|||||||
if (willDo === 'checkout') {
|
if (willDo === 'checkout') {
|
||||||
onCheckout();
|
onCheckout();
|
||||||
}
|
}
|
||||||
if(setWillDo) setWillDo(''); // Reset willDo after handling
|
if (setWillDo) setWillDo('');
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const priceColor = product.price === 0 ? '#059669' : '#2563eb';
|
const priceColor = product.price === 0 ? '#059669' : '#2563eb';
|
||||||
console.log(product)
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
|
{/* Default view */}
|
||||||
{!showChildSelector && !showSubscriptionSelector && !showNamingInput && (
|
{!showChildSelector && !showSubscriptionSelector && !showNamingInput && (
|
||||||
<>
|
<>
|
||||||
<div className={styles.image} style={{ backgroundImage: `url(${product.image})` }}></div>
|
<div className={styles.image} style={{ backgroundImage: `url(${product.image})` }}></div>
|
||||||
@@ -182,38 +169,30 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
|||||||
sub.product_id === product.id || sub.product_parent_id === product.id
|
sub.product_id === product.id || sub.product_parent_id === product.id
|
||||||
) && product.end_date ? 'Perpanjang' : 'Checkout'}
|
) && product.end_date ? 'Perpanjang' : 'Checkout'}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Child selector */}
|
||||||
{showChildSelector && (
|
{showChildSelector && (
|
||||||
<div className={styles.childSelector}>
|
<div className={styles.childSelector}>
|
||||||
<h3>Pilih Paket</h3>
|
<h3>Pilih Paket</h3>
|
||||||
{product.children.map(child => (
|
{product.children.map(child => (
|
||||||
<label key={child.id} className={styles.childProduct}>
|
<label key={child.id} className={styles.childProduct}>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="radio"
|
||||||
value={child.id}
|
value={child.id}
|
||||||
checked={selectedChildIds.includes(child.id)}
|
checked={selectedChildIds.includes(child.id)}
|
||||||
onChange={e => {
|
onChange={() => setSelectedChildIds([child.id])}
|
||||||
const checked = e.target.checked;
|
|
||||||
setSelectedChildIds(prev =>
|
|
||||||
checked ? [...prev, child.id] : prev.filter(id => id !== child.id)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
{child.name} — Rp {parseInt(child.price || 0).toLocaleString('id-ID')}
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
<div> {child.name}</div>
|
||||||
|
<div>Rp {parseInt(child.price || 0).toLocaleString('id-ID')}</div>
|
||||||
|
</div>
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
<p>
|
|
||||||
<strong>Total Harga:</strong> Rp {selectedChildIds
|
|
||||||
.map(id => product.children.find(child => child.id === id)?.price || 0)
|
|
||||||
.reduce((a, b) => a + b, 0)
|
|
||||||
.toLocaleString('id-ID')}
|
|
||||||
</p>
|
|
||||||
<div className={styles.buttonGroup}>
|
<div className={styles.buttonGroup}>
|
||||||
<button className={styles.button} onClick={() => setShowChildSelector(false)}>
|
<button className={styles.button} onClick={() => { setShowChildSelector(false); setShowNamingInput(true); }}>
|
||||||
Kembali
|
Kembali
|
||||||
</button>
|
</button>
|
||||||
<button className={styles.button} onClick={onConfirmChildren}>
|
<button className={styles.button} onClick={onConfirmChildren}>
|
||||||
@@ -223,42 +202,32 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Subscription selector */}
|
||||||
{showSubscriptionSelector && !showNamingInput && (
|
{showSubscriptionSelector && !showNamingInput && (
|
||||||
<div className={styles.childSelector}>
|
<div className={styles.childSelector}>
|
||||||
<h5>Perpanjang {product.name.split('%%%')[0]} </h5>
|
<h5>Kamu sudah punya produk ini</h5>
|
||||||
{matchingSubscriptions.map(sub => (
|
<div className={styles.childProduct} onClick={() => { setShowedModal(''); navigate('/dashboard') }}>
|
||||||
<label key={sub.id} className={styles.childProduct}>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<input
|
<div>Perpanjang produk ini</div>
|
||||||
type="radio"
|
<div>➔</div>
|
||||||
name="subscription"
|
</div>
|
||||||
value={sub.id}
|
</div>
|
||||||
checked={selectedSubscriptionId == sub.id}
|
<h6>Atau</h6>
|
||||||
onChange={() => { setSelectedSubscriptionId(sub.id); setCustomName(sub.product_name) }}
|
<label className={styles.childProduct} onClick={() => { setSelectedSubscriptionId(0); onConfirmSelector(); }}>
|
||||||
/>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
{sub.product_name.split('%%%')[0]}
|
<div>Tambah {product.name.split('%%%')[0]} baru</div>
|
||||||
</label>
|
<div>➔</div>
|
||||||
))}
|
</div>
|
||||||
<h6>Atau buat baru</h6>
|
|
||||||
<label className={styles.childProduct}>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="subscription"
|
|
||||||
checked={selectedSubscriptionId === 0}
|
|
||||||
onChange={() => {setSelectedSubscriptionId(0); console.log(product.id)}}
|
|
||||||
/>
|
|
||||||
Buat {product.name.split('%%%')[0]} baru
|
|
||||||
</label>
|
</label>
|
||||||
<div className={styles.buttonGroup}>
|
<div className={styles.buttonGroup}>
|
||||||
<button className={styles.button} onClick={() => setShowSubscriptionSelector(false)}>
|
<button className={styles.button} onClick={() => setShowSubscriptionSelector(false)}>
|
||||||
Kembali
|
Kembali
|
||||||
</button>
|
</button>
|
||||||
<button className={styles.button} onClick={onConfirmSelector}>
|
|
||||||
Lanjut ke Checkout
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Naming input */}
|
||||||
{showNamingInput && (
|
{showNamingInput && (
|
||||||
<div className={styles.childSelector}>
|
<div className={styles.childSelector}>
|
||||||
<h5>Buat {product.name.split('%%%')[0]} Baru</h5>
|
<h5>Buat {product.name.split('%%%')[0]} Baru</h5>
|
||||||
@@ -271,47 +240,20 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
|||||||
style={{ width: '100%', padding: '8px', marginBottom: '16px', borderRadius: '10px' }}
|
style={{ width: '100%', padding: '8px', marginBottom: '16px', borderRadius: '10px' }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
|
||||||
matchingSubscriptions.some(
|
|
||||||
(sub) => sub.product_name === `${product.name}@${customName}`
|
|
||||||
) && (
|
|
||||||
<p style={{ color: 'red', marginBottom: '10px' }}>
|
|
||||||
Nama produk sudah digunakan.
|
|
||||||
</p>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
<div className={styles.buttonGroup}>
|
<div className={styles.buttonGroup}>
|
||||||
<button
|
<button className={styles.button} onClick={() => setShowNamingInput(false)}>
|
||||||
className={styles.button}
|
|
||||||
onClick={() => {
|
|
||||||
setShowNamingInput(false);
|
|
||||||
|
|
||||||
const hasMatchingSubscription = Array.isArray(subscriptions) &&
|
|
||||||
subscriptions.some(sub =>
|
|
||||||
String(sub.product_id) === String(product.id) || String(sub.product_parent_id) === String(product.id)
|
|
||||||
);
|
|
||||||
if (hasMatchingSubscription) setShowSubscriptionSelector(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Kembali
|
Kembali
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={styles.button}
|
className={styles.button}
|
||||||
onClick={onFinalCheckoutNewProduct}
|
onClick={onFinalCheckoutNewProduct}
|
||||||
disabled={
|
disabled={customName.trim() === ''}
|
||||||
customName.trim() === '' ||
|
|
||||||
matchingSubscriptions.some(
|
|
||||||
(sub) => sub.product_name === `${product.name}@${customName}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
Checkout
|
Lanjut
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -91,7 +91,6 @@
|
|||||||
.carouselContainer {
|
.carouselContainer {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 0 56px;
|
|
||||||
min-height: 380px; /* compact */
|
min-height: 380px; /* compact */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +132,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.carouselContainer {
|
.carouselContainer {
|
||||||
padding: 0 60px;
|
|
||||||
min-height: 420px;
|
min-height: 420px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +165,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.carouselContainer {
|
.carouselContainer {
|
||||||
padding: 0 50px;
|
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +207,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.carouselContainer {
|
.carouselContainer {
|
||||||
padding: 0 40px;
|
|
||||||
min-height: 370px;
|
min-height: 370px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,7 +249,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.carouselContainer {
|
.carouselContainer {
|
||||||
padding: 0 30px;
|
|
||||||
min-height: 320px;
|
min-height: 320px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,7 +291,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.carouselContainer {
|
.carouselContainer {
|
||||||
padding: 0 25px;
|
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -421,7 +421,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.currentPrice {
|
.currentPrice {
|
||||||
font-size: 0.95rem;
|
font-size: 1.2rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #2563eb;
|
color: #2563eb;
|
||||||
}
|
}
|
||||||
@@ -950,3 +950,139 @@
|
|||||||
max-width: 150px; /* biar logo tidak terlalu besar */
|
max-width: 150px; /* biar logo tidak terlalu besar */
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Navigation Tabs */
|
||||||
|
.navTabs {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #2a4fd6; /* blue bar like screenshot */
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 6px 6px 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floatMenuItem {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #ffffffcc; /* light white text */
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s ease, color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floatMenuItem:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floatMenuItemActive {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #2a4fd6; /* active text color */
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floatMenuTitle {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floatMenuIcon {
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections */
|
||||||
|
.Section {
|
||||||
|
padding: 24px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 0 0 6px 6px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Settings Page Layout */
|
||||||
|
.profileSection {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profileHeading {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2937;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionDivider {
|
||||||
|
height: 2px;
|
||||||
|
background-color: #2a4fd6;
|
||||||
|
width: 40px;
|
||||||
|
margin: 8px 0 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formGrid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 16px;
|
||||||
|
background-color: #f6f9fc;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullWidth {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #374151;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputField,
|
||||||
|
.selectField {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selectField {
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saveButton {
|
||||||
|
background-color: #2a4fd6;
|
||||||
|
color: white;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 10px 24px;
|
||||||
|
border-radius: 50px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 16px;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saveButton:hover {
|
||||||
|
background-color: #223fa9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Change Password Section */
|
||||||
|
.changePasswordSection {
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonRow {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,172 +1,425 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from "react";
|
||||||
import ProductDetailPage from '../ProductDetailPage';
|
import styles from "../Styles.module.css";
|
||||||
import Login from '../Login';
|
import { Box, Settings, ShoppingCart } from "lucide-react";
|
||||||
import styles from '../Styles.module.css';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
const CoursePage = ({ subscriptions, setSelectedProduct, setShowedModal}) => {
|
|
||||||
const [hoveredCard, setHoveredCard] = useState(null);
|
|
||||||
const [products, setProducts] = useState([]);
|
|
||||||
|
|
||||||
// Buka modal otomatis berdasarkan query
|
const Dashboard = ({
|
||||||
useEffect(() => {
|
subscriptions,
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
setSelectedProduct,
|
||||||
const modal = urlParams.get('modal');
|
setShowedModal,
|
||||||
const productId = urlParams.get('product_id');
|
userData,
|
||||||
|
setWillDo
|
||||||
|
}) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [activeTab, setActiveTab] = useState("products");
|
||||||
|
const [hoveredCard, setHoveredCard] = useState(null);
|
||||||
|
const [products, setProducts] = useState([]);
|
||||||
|
|
||||||
if (modal === 'product' && productId && products.length > 0) {
|
// User Settings form state
|
||||||
const product = products.find(p => String(p.id) === productId);
|
const [settings, setSettings] = useState({
|
||||||
if (product) {
|
username: "",
|
||||||
setSelectedProduct(product);
|
email: "",
|
||||||
setShowedModal('product');
|
password: "",
|
||||||
|
profile_data: {
|
||||||
|
name: "",
|
||||||
|
image: "",
|
||||||
|
phone: "",
|
||||||
|
address: "",
|
||||||
|
company: ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (userData) {
|
||||||
|
setSettings(userData);
|
||||||
|
}
|
||||||
|
}, [userData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!subscriptions) return;
|
||||||
|
|
||||||
|
function groupSubscriptionsByProductName(subs) {
|
||||||
|
const result = {};
|
||||||
|
subs.forEach((sub) => {
|
||||||
|
const name = sub.product_name;
|
||||||
|
const productId = sub.product_id;
|
||||||
|
if (!result[name]) {
|
||||||
|
result[name] = {
|
||||||
|
product_id: productId,
|
||||||
|
product_name: name,
|
||||||
|
unit_type: sub.unit_type,
|
||||||
|
end_date: sub.end_date,
|
||||||
|
quantity: 0,
|
||||||
|
subscriptions: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentEnd = new Date(result[name].end_date);
|
||||||
|
const thisEnd = new Date(sub.end_date);
|
||||||
|
if (thisEnd > currentEnd) {
|
||||||
|
result[name].end_date = sub.end_date;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub.unit_type === "token") {
|
||||||
|
result[name].quantity += sub.quantity ?? 0;
|
||||||
|
} else {
|
||||||
|
result[name].quantity += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result[name].subscriptions.push(sub);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupedSubs = groupSubscriptionsByProductName(subscriptions);
|
||||||
|
const productIds = [...new Set(subscriptions.map((s) => s.product_id))];
|
||||||
|
|
||||||
|
fetch(
|
||||||
|
"https://bot.kediritechnopark.com/webhook/store-production/products",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ itemsId: productIds})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
// Step 1: Enrich base products (without children yet)
|
||||||
|
const enrichedData = Object.values(groupedSubs)
|
||||||
|
.filter((group) => data.some((p) => p.id === group.product_id))
|
||||||
|
.map((group) => {
|
||||||
|
const productData = data.find((p) => p.id == group.product_id);
|
||||||
|
let image = productData?.image || "";
|
||||||
|
let description = productData?.description || "";
|
||||||
|
let site_url = productData?.site_url || "";
|
||||||
|
if (!image && productData?.sub_product_of) {
|
||||||
|
const parent = data.find(
|
||||||
|
(p) => p.id === productData.sub_product_of
|
||||||
|
);
|
||||||
|
image = parent?.image || "";
|
||||||
|
description = parent?.description || "";
|
||||||
|
site_url = parent?.site_url || "";
|
||||||
}
|
}
|
||||||
}
|
return {
|
||||||
}, [products]);
|
executeCheckout: group.product_name,
|
||||||
|
id: group.product_id,
|
||||||
|
name: group.product_name,
|
||||||
|
type: productData?.type || "product",
|
||||||
|
image,
|
||||||
|
description,
|
||||||
|
site_url,
|
||||||
|
price: productData?.price || 0,
|
||||||
|
currency: productData?.currency || "IDR",
|
||||||
|
duration: productData?.duration || {},
|
||||||
|
sub_product_of: productData?.sub_product_of || null,
|
||||||
|
is_visible: productData?.is_visible ?? true,
|
||||||
|
unit_type: productData?.unit_type || group.unit_type,
|
||||||
|
quantity: group.quantity,
|
||||||
|
end_date: group.end_date,
|
||||||
|
children: []
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
// Step 2: Create a quick lookup table for enrichedData
|
||||||
if (!subscriptions) return;
|
const productMap = {};
|
||||||
|
enrichedData.forEach((p) => {
|
||||||
|
console.log(p)
|
||||||
|
productMap[p.name.split("%%%")[1]] = p;
|
||||||
|
});
|
||||||
|
|
||||||
function groupSubscriptionsByProductName(subs) {
|
// Step 3: Find children in API `data` and attach to parents
|
||||||
const result = {};
|
data
|
||||||
subs.forEach(sub => {
|
.filter((p) => p.sub_product_of) // only those with a parent
|
||||||
const name = sub.product_name;
|
.forEach((child) => {
|
||||||
const productId = sub.product_id;
|
// ✅ Current logic — attach to the real parent
|
||||||
if (!result[name]) {
|
const parent = productMap[child.sub_product_of];
|
||||||
result[name] = {
|
if (parent) {
|
||||||
product_id: productId,
|
parent.children.push(child);
|
||||||
product_name: name,
|
}
|
||||||
unit_type: sub.unit_type,
|
// ➕ New logic — attach to other products with the same sub_product_of
|
||||||
end_date: sub.end_date,
|
Object.values(productMap).forEach((possibleParent) => {
|
||||||
quantity: 0,
|
if (
|
||||||
subscriptions: []
|
possibleParent.id !== child.id && // not itself
|
||||||
};
|
possibleParent.sub_product_of === child.sub_product_of // same parent reference
|
||||||
}
|
) {
|
||||||
|
possibleParent.children.push(child);
|
||||||
const currentEnd = new Date(result[name].end_date);
|
}
|
||||||
const thisEnd = new Date(sub.end_date);
|
|
||||||
if (thisEnd > currentEnd) {
|
|
||||||
result[name].end_date = sub.end_date;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sub.unit_type == 'token') {
|
|
||||||
result[name].quantity += sub.quantity ?? 0;
|
|
||||||
} else {
|
|
||||||
result[name].quantity += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
result[name].subscriptions.push(sub);
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
console.log(enrichedData)
|
||||||
|
setProducts(enrichedData);
|
||||||
|
})
|
||||||
|
.catch((err) => console.error("Fetch error:", err));
|
||||||
|
|
||||||
return result;
|
}, [subscriptions]);
|
||||||
}
|
|
||||||
|
|
||||||
const groupedSubs = groupSubscriptionsByProductName(subscriptions);
|
const handleSettingsChange = (field, value, nested = false) => {
|
||||||
const productIds = [...new Set(subscriptions.map(s => s.product_id))];
|
if (nested) {
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
profile_data: { ...prev.profile_data, [field]: value }
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
setSettings((prev) => ({ ...prev, [field]: value }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
fetch('https://bot.kediritechnopark.com/webhook/store-production/products', {
|
const saveSettings = () => {
|
||||||
method: 'POST',
|
fetch(
|
||||||
headers: {
|
"https://bot.kediritechnopark.com/webhook-test/user-production/data",
|
||||||
'Content-Type': 'application/json',
|
{
|
||||||
},
|
method: "PUT",
|
||||||
body: JSON.stringify({ itemsId: productIds }),
|
headers: {
|
||||||
})
|
"Content-Type": "application/json"
|
||||||
.then(res => res.json())
|
},
|
||||||
.then(data => {
|
body: JSON.stringify(settings)
|
||||||
const enrichedData = Object.values(groupedSubs)
|
}
|
||||||
.filter(group => data.some(p => p.id === group.product_id))
|
)
|
||||||
.map(group => {
|
.then((res) => res.json())
|
||||||
const productData = data.find(p => p.id == group.product_id);
|
.then(() => {
|
||||||
let image = productData?.image || '';
|
alert("Settings updated successfully!");
|
||||||
let description = productData?.description || '';
|
})
|
||||||
let site_url = productData?.site_url || '';
|
.catch((err) => alert("Error updating settings: " + err));
|
||||||
if (!image && productData?.sub_product_of) {
|
};
|
||||||
const parent = data.find(p => p.id === productData.sub_product_of);
|
|
||||||
console.log(parent)
|
|
||||||
image = parent?.image || '';
|
|
||||||
description = parent?.description || '';
|
|
||||||
site_url = parent?.site_url || '';
|
|
||||||
}
|
|
||||||
console.log(site_url)
|
|
||||||
return {
|
|
||||||
id: group.product_id,
|
|
||||||
name: group.product_name,
|
|
||||||
type: productData?.type || 'product',
|
|
||||||
image: image,
|
|
||||||
description: description,
|
|
||||||
site_url: site_url,
|
|
||||||
price: productData?.price || 0,
|
|
||||||
currency: productData?.currency || 'IDR',
|
|
||||||
duration: productData?.duration || {},
|
|
||||||
sub_product_of: productData?.sub_product_of || null,
|
|
||||||
is_visible: productData?.is_visible ?? true,
|
|
||||||
unit_type: productData?.unit_type || group.unit_type,
|
|
||||||
quantity: group.quantity,
|
|
||||||
end_date: group.end_date,
|
|
||||||
children: [],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
console.log(enrichedData)
|
|
||||||
setProducts(enrichedData);
|
|
||||||
})
|
|
||||||
.catch(err => console.error('Fetch error:', err));
|
|
||||||
}, [subscriptions]);
|
|
||||||
|
|
||||||
const features = [/* ... (tidak diubah) ... */];
|
return (
|
||||||
|
<div style={{ fontFamily: "Inter, system-ui, sans-serif" }}>
|
||||||
|
{/* Tabs Navigation */}
|
||||||
|
<div className={styles.navTabs}>
|
||||||
|
<button
|
||||||
|
className={`${styles.floatMenuItem} ${activeTab === "products" ? styles.floatMenuItemActive : ""
|
||||||
|
}`}
|
||||||
|
onClick={() => setActiveTab("products")}
|
||||||
|
>
|
||||||
|
<span className={styles.floatMenuTitle}>Produk Saya</span>
|
||||||
|
<Box size={16} className={styles.floatMenuIcon} />
|
||||||
|
</button>
|
||||||
|
|
||||||
return (
|
<button
|
||||||
<div style={{ fontFamily: 'Inter, system-ui, sans-serif' }}>
|
className={`${styles.floatMenuItem} ${activeTab === "settings" ? styles.floatMenuItemActive : ""
|
||||||
{/* Courses Section */}
|
}`}
|
||||||
<section className={styles.Section}>
|
onClick={() => setActiveTab("settings")}
|
||||||
<div className={styles.coursesContainer}>
|
>
|
||||||
<h2 className={styles.coursesTitle}>MY PRODUCTS</h2>
|
<span className={styles.floatMenuTitle}>Profil Pengguna</span>
|
||||||
<div className={styles.coursesGrid}>
|
<Settings size={16} className={styles.floatMenuIcon} />
|
||||||
{products &&
|
</button>
|
||||||
products[0]?.name &&
|
|
||||||
products.map(product => (
|
|
||||||
<div
|
|
||||||
key={product.name}
|
|
||||||
className={`${styles.courseCard} ${hoveredCard === product.name ? styles.courseCardHover : ''}`}
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedProduct(product);
|
|
||||||
setShowedModal('product');
|
|
||||||
}}
|
|
||||||
onMouseEnter={() => setHoveredCard(product.name)}
|
|
||||||
onMouseLeave={() => setHoveredCard(null)}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<div className={styles.courseImage} style={{ backgroundImage: `url(${product.image})` }} />
|
|
||||||
<div className={styles.courseContentTop}>
|
|
||||||
<h3 className={styles.courseTitle}>{product.name.split('%%%')[0]}</h3>
|
|
||||||
<p className={styles.courseDesc}>{product.description}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={styles.courseContentBottom}>
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<button className="px-4 py-2 rounded-pill text-white" style={{ background: 'linear-gradient(to right, #6a59ff, #8261ee)', border: 'none' }}
|
<button
|
||||||
onClick={() => {
|
className={`${styles.floatMenuItem} ${activeTab === "orders" ? styles.floatMenuItemActive : ""
|
||||||
setSelectedProduct(product);
|
}`}
|
||||||
setShowedModal('product');
|
onClick={() => setActiveTab("orders")}
|
||||||
}}>Perpanjang</button>
|
>
|
||||||
</div>
|
<span className={styles.floatMenuTitle}>Pembelian</span>
|
||||||
</div>
|
<ShoppingCart size={16} className={styles.floatMenuIcon} />
|
||||||
))}
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Tab Content */}
|
||||||
|
{activeTab === "products" && (
|
||||||
|
<section className={styles.Section}>
|
||||||
|
<div className={styles.coursesContainer}>
|
||||||
|
<div className={styles.coursesGrid}>
|
||||||
|
{products.map((product) => (
|
||||||
|
<div
|
||||||
|
key={product.name}
|
||||||
|
className={`${styles.courseCard} ${hoveredCard === product.name ? styles.courseCardHover : ""
|
||||||
|
}`}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedProduct(product);
|
||||||
|
setShowedModal("product");
|
||||||
|
}}
|
||||||
|
onMouseEnter={() => setHoveredCard(product.name)}
|
||||||
|
onMouseLeave={() => setHoveredCard(null)}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
className={styles.courseImage}
|
||||||
|
style={{
|
||||||
|
backgroundImage: `url(${product.image})`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className={styles.courseContentTop}>
|
||||||
|
<h3 className={styles.courseTitle}>
|
||||||
|
{product.name.split("%%%")[0]}
|
||||||
|
</h3>
|
||||||
|
<p className={styles.courseDesc}>
|
||||||
|
{product.description}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.courseContentBottom}>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="px-4 py-2 rounded-pill text-white"
|
||||||
|
style={{
|
||||||
|
background:
|
||||||
|
"linear-gradient(to right, #6a59ff, #8261ee)",
|
||||||
|
border: "none"
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedProduct(product);
|
||||||
|
setShowedModal("product");
|
||||||
|
setWillDo('checkout');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Perpanjang
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
))}
|
||||||
</div >
|
<div
|
||||||
);
|
className={`${styles.courseCard} ${hoveredCard === 0 ? styles.courseCardHover : ""
|
||||||
|
}`}
|
||||||
|
onMouseEnter={() => setHoveredCard(0)}
|
||||||
|
onMouseLeave={() => setHoveredCard(null)}
|
||||||
|
onClick={() => navigate('/?tab=products')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
+ Tambah produk baru</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{activeTab === "settings" && (
|
||||||
|
<section className={styles.profileSection}>
|
||||||
|
<h2 className={styles.profileHeading}>Profil</h2>
|
||||||
|
<div className={styles.sectionDivider}></div>
|
||||||
|
|
||||||
|
<div className={styles.formGrid}>
|
||||||
|
<div>
|
||||||
|
<label className={styles.label}>Username</label>
|
||||||
|
<input
|
||||||
|
className={styles.inputField}
|
||||||
|
value={settings.username}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleSettingsChange("username", e.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className={styles.label}>Email</label>
|
||||||
|
<input
|
||||||
|
className={styles.inputField}
|
||||||
|
value={settings.email}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleSettingsChange("email", e.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className={styles.label}>Full Name</label>
|
||||||
|
<input
|
||||||
|
className={styles.inputField}
|
||||||
|
value={settings.profile_data.name}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleSettingsChange("name", e.target.value, true)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className={styles.label}>Phone</label>
|
||||||
|
<input
|
||||||
|
className={styles.inputField}
|
||||||
|
value={settings.profile_data.phone}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleSettingsChange("phone", e.target.value, true)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.fullWidth}>
|
||||||
|
<label className={styles.label}>Address</label>
|
||||||
|
<input
|
||||||
|
className={styles.inputField}
|
||||||
|
value={settings.profile_data.address}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleSettingsChange("address", e.target.value, true)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className={styles.label}>Company</label>
|
||||||
|
<input
|
||||||
|
className={styles.inputField}
|
||||||
|
value={settings.profile_data.company}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleSettingsChange("company", e.target.value, true)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className={styles.label}>Profile Image URL</label>
|
||||||
|
<input
|
||||||
|
className={styles.inputField}
|
||||||
|
value={settings.profile_data.image}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleSettingsChange("image", e.target.value, true)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 className={styles.profileHeading}>Ganti password</h2>
|
||||||
|
<div className={styles.sectionDivider}></div>
|
||||||
|
<div className={styles.formGrid}>
|
||||||
|
<div>
|
||||||
|
<label className={styles.label}>New Password</label>
|
||||||
|
<input
|
||||||
|
className={styles.inputField}
|
||||||
|
type="password"
|
||||||
|
value={settings.password}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleSettingsChange("password", e.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className={styles.label}>Re-type New Password</label>
|
||||||
|
<input
|
||||||
|
className={styles.inputField}
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button className={styles.saveButton} onClick={saveSettings}>
|
||||||
|
Save Changes
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{activeTab === "orders" && (
|
||||||
|
<section className={styles.Section}>
|
||||||
|
<h2>My Orders</h2>
|
||||||
|
<p>Orders list will be displayed here.</p>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CoursePage;
|
export default Dashboard;
|
||||||
|
|||||||
Reference in New Issue
Block a user