ok
This commit is contained in:
19
src/App.js
19
src/App.js
@@ -20,11 +20,7 @@ import ProductsPage from './components/pages/ProductsPage';
|
||||
import processProducts from './helper/processProducts';
|
||||
|
||||
function HomePage({
|
||||
hoveredCard,
|
||||
setHoveredCard,
|
||||
selectedProduct,
|
||||
setSelectedProduct,
|
||||
showedModal,
|
||||
setShowedModal,
|
||||
productSectionRef,
|
||||
courseSectionRef,
|
||||
@@ -38,16 +34,12 @@ function HomePage({
|
||||
<ServicesSection />
|
||||
<ProductSection
|
||||
productSectionRef={productSectionRef}
|
||||
hoveredCard={hoveredCard}
|
||||
setHoveredCard={setHoveredCard}
|
||||
setSelectedProduct={setSelectedProduct}
|
||||
setShowedModal={setShowedModal}
|
||||
setWillDo={setWillDo}
|
||||
/>
|
||||
<AcademySection
|
||||
courseSectionRef={courseSectionRef}
|
||||
hoveredCard={hoveredCard}
|
||||
setHoveredCard={setHoveredCard}
|
||||
setSelectedProduct={setSelectedProduct}
|
||||
setShowedModal={setShowedModal}
|
||||
setWillDo={setWillDo}
|
||||
@@ -76,7 +68,6 @@ function parseJwt(token) {
|
||||
|
||||
function App() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [hoveredCard, setHoveredCard] = useState(null);
|
||||
const [subscriptions, setSubscriptions] = useState(null);
|
||||
const [selectedProduct, setSelectedProduct] = useState({});
|
||||
const [showedModal, setShowedModal] = useState(null);
|
||||
@@ -303,11 +294,7 @@ function App() {
|
||||
path="/"
|
||||
element={
|
||||
<HomePage
|
||||
hoveredCard={hoveredCard}
|
||||
setHoveredCard={setHoveredCard}
|
||||
selectedProduct={selectedProduct}
|
||||
setSelectedProduct={setSelectedProduct}
|
||||
showedModal={showedModal}
|
||||
setShowedModal={setShowedModal}
|
||||
productSectionRef={productSectionRef}
|
||||
courseSectionRef={courseSectionRef}
|
||||
@@ -315,9 +302,11 @@ function App() {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route path="/products" element={<ProductsPage subscriptions={subscriptions} />} />
|
||||
<Route path="/dashboard" element={<ProductsPage
|
||||
setShowedModal={setShowedModal}
|
||||
setSelectedProduct={setSelectedProduct} subscriptions={subscriptions} />} />
|
||||
<Route
|
||||
path="/dashboard"
|
||||
path="/admin"
|
||||
element={
|
||||
<Dashboard
|
||||
setShowedModal={(e, productId) => {
|
||||
|
||||
@@ -2,9 +2,10 @@ import React, { useEffect, useState } from 'react';
|
||||
import { Container } from 'react-bootstrap';
|
||||
import styles from './Styles.module.css';
|
||||
|
||||
const AcademySection = ({hoveredCard, setHoveredCard, setSelectedProduct, setShowedModal, courseSectionRef, setWillDo}) => {
|
||||
const AcademySection = ({setSelectedProduct, setShowedModal, courseSectionRef, setWillDo}) => {
|
||||
const [products, setProducts] = useState([]);
|
||||
|
||||
const [hoveredCard, setHoveredCard] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('https://bot.kediritechnopark.com/webhook/store-production/products', {
|
||||
method: 'POST',
|
||||
|
||||
191
src/components/Dashboard copy.js
Normal file
191
src/components/Dashboard copy.js
Normal file
@@ -0,0 +1,191 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { TrendingUp, TrendingDown, DollarSign, ShoppingCart, Users, Plus, GitBranchPlus } from 'lucide-react';
|
||||
import styles from './Dashboard.module.css';
|
||||
import processProducts from '../helper/processProducts';
|
||||
|
||||
/**
|
||||
* Props:
|
||||
* - setShowedModal: (modalName: string, productId?: string|number) => void
|
||||
*/
|
||||
const Dashboard = ({ setShowedModal }) => {
|
||||
const [unitType, setUnitType] = useState('duration'); // kept for potential reuse
|
||||
const [durationUnit, setDurationUnit] = useState('days'); // kept for potential reuse
|
||||
const [availableTypes, setAvailableTypes] = useState([]);
|
||||
const [availableGroups, setAvailableGroups] = useState([]);
|
||||
const [selectedType, setSelectedType] = useState(null);
|
||||
const [selectedGroup, setSelectedGroup] = useState(null);
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
const [products, setProducts] = useState([]);
|
||||
|
||||
const [dashboardData, setDashboardData] = useState({
|
||||
totalRevenue: { amount: 10215845, currency: 'IDR', change: 33.87, period: '22 - 29 May 2025' },
|
||||
totalItemsSold: { amount: 128980, change: -33.87, period: '22 - 29 May 2025' },
|
||||
totalVisitors: { amount: 2905897, change: 33.87, period: '22 - 29 May 2025' },
|
||||
chartData: [
|
||||
{ date: '22/06', items: 200, revenue: 800 },
|
||||
{ date: '23/06', items: 750, revenue: 450 },
|
||||
{ date: '24/06', items: 550, revenue: 200 },
|
||||
{ date: '25/06', items: 300, revenue: 350 },
|
||||
{ date: '26/06', items: 900, revenue: 450 },
|
||||
{ date: '27/06', items: 550, revenue: 200 },
|
||||
],
|
||||
latestTransactions: []
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const fetchDistinctOptions = async () => {
|
||||
const match = document.cookie.match(new RegExp('(^| )token=([^;]+)'));
|
||||
if (!match) return;
|
||||
const token = match[2];
|
||||
|
||||
try {
|
||||
const res = await fetch('https://bot.kediritechnopark.com/webhook/store-production/get-products', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await res.json();
|
||||
const productsArr = result || [];
|
||||
|
||||
const types = [...new Set(productsArr.map(p => p.type).filter(Boolean))];
|
||||
const groups = [...new Set(productsArr.map(p => p.group).filter(Boolean))];
|
||||
|
||||
setAvailableTypes(types);
|
||||
setAvailableGroups(groups);
|
||||
setProducts(processProducts(productsArr));
|
||||
} catch (err) {
|
||||
console.error('Gagal ambil produk:', err);
|
||||
}
|
||||
};
|
||||
|
||||
fetchDistinctOptions();
|
||||
}, []);
|
||||
|
||||
const formatCurrency = (amount) => new Intl.NumberFormat('id-ID').format(amount);
|
||||
|
||||
const getStatusClass = (status) => {
|
||||
switch (status) {
|
||||
case 'confirmed': return styles.statusConfirmed;
|
||||
case 'waiting payment': return styles.statusWaiting;
|
||||
case 'payment expired': return styles.statusExpired;
|
||||
default: return styles.statusConfirmed;
|
||||
}
|
||||
};
|
||||
|
||||
const StatCard = ({ title, value, currency, change, period, icon: Icon, isNegative }) => (
|
||||
<div className={styles.statCard}>
|
||||
<div className={styles.statCardHeader}>
|
||||
<h3 className={styles.statCardTitle}>{title}</h3>
|
||||
<Icon className={styles.statCardIcon} />
|
||||
</div>
|
||||
<div className={styles.statCardValue}>
|
||||
{currency && `${currency} `}{formatCurrency(value)}
|
||||
</div>
|
||||
<div className={styles.statCardFooter}>
|
||||
<div className={styles.statCardChange}>
|
||||
{isNegative ? (
|
||||
<TrendingDown className={`${styles.trendIcon} ${styles.trendDown}`} />
|
||||
) : (
|
||||
<TrendingUp className={`${styles.trendIcon} ${styles.trendUp}`} />
|
||||
)}
|
||||
<span className={`${styles.changeText} ${isNegative ? styles.changeTextNegative : styles.changeTextPositive}`}>
|
||||
{Math.abs(change)}%
|
||||
</span>
|
||||
<span className={styles.fromLastWeek}>from last week</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.statCardPeriod}>{period}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const BarChart = ({ data }) => {
|
||||
const maxValue = Math.max(...data.map(item => Math.max(item.items, item.revenue)));
|
||||
return (
|
||||
<div className={styles.barChart}>
|
||||
{data.map((item, index) => (
|
||||
<div key={index} className={styles.barGroup}>
|
||||
<div className={styles.barContainer}>
|
||||
<div className={`${styles.bar} ${styles.barItems}`} style={{ height: `${(item.items / maxValue) * 200}px` }} />
|
||||
<div className={`${styles.bar} ${styles.barRevenue}`} style={{ height: `${(item.revenue / maxValue) * 200}px` }} />
|
||||
</div>
|
||||
<span className={styles.barLabel}>{item.date}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.statsGrid}>
|
||||
<StatCard title="Total Revenue" value={dashboardData.totalRevenue.amount} currency="IDR" change={dashboardData.totalRevenue.change} period={dashboardData.totalRevenue.period} icon={DollarSign} isNegative={false} />
|
||||
<StatCard title="Total Items Sold" value={dashboardData.totalItemsSold.amount} change={dashboardData.totalItemsSold.change} period={dashboardData.totalItemsSold.period} icon={ShoppingCart} isNegative={true} />
|
||||
<StatCard title="Total Visitor" value={dashboardData.totalVisitors.amount} change={dashboardData.totalVisitors.change} period={dashboardData.totalVisitors.period} icon={Users} isNegative={false} />
|
||||
</div>
|
||||
|
||||
<div className={styles.chartsGrid}>
|
||||
{/* Tempatkan <BarChart data={dashboardData.chartData} /> jika mau ditampilkan */}
|
||||
</div>
|
||||
|
||||
{/* Products List */}
|
||||
<div className={styles.chartCard}>
|
||||
<div className={styles.transactionsHeader}>
|
||||
<h3 className={styles.transactionsTitle}>Products</h3>
|
||||
|
||||
{/* Tombol "Buat Item" → buka modal create */}
|
||||
<button
|
||||
type="button"
|
||||
className={styles.primaryButton}
|
||||
onClick={() => setShowedModal && setShowedModal('create-item')}
|
||||
title="Buat produk baru"
|
||||
>
|
||||
<Plus size={16} style={{ marginRight: 6 }} />
|
||||
Buat Item
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={styles.transactionsList}>
|
||||
{products.map((product) => (
|
||||
<div key={product.id} className={styles.transactionItem}>
|
||||
<div className={styles.transactionLeft}>
|
||||
<div className={styles.transactionInfo}>
|
||||
<h4>{product.name}</h4>
|
||||
{product.children && product.children.map((child) => (
|
||||
<p key={child.id}>- {child.name}</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.transactionRight}>
|
||||
<span className={styles.transactionAmount}>
|
||||
IDR {formatCurrency(product.price)}
|
||||
</span>
|
||||
<div className={`${styles.statusIndicator} ${getStatusClass(product.status)}`}></div>
|
||||
<span className={styles.transactionStatus}>{product.status}</span>
|
||||
|
||||
{/* Tombol "Add Child" → buka modal create dengan parent product_id */}
|
||||
<button
|
||||
type="button"
|
||||
className={styles.secondaryButton}
|
||||
onClick={() => setShowedModal('create-item', product.id)}
|
||||
title="Tambah sub-produk"
|
||||
style={{ marginLeft: '0.75rem' }}
|
||||
>
|
||||
<GitBranchPlus size={16} style={{ marginRight: 6 }} />
|
||||
Add Child
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bagian form create yang lama sudah DIPINDAH ke halaman/komponen baru */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
@@ -1,191 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { TrendingUp, TrendingDown, DollarSign, ShoppingCart, Users, Plus, GitBranchPlus } from 'lucide-react';
|
||||
import styles from './Dashboard.module.css';
|
||||
import processProducts from '../helper/processProducts';
|
||||
|
||||
/**
|
||||
* Props:
|
||||
* - setShowedModal: (modalName: string, productId?: string|number) => void
|
||||
*/
|
||||
const Dashboard = ({ setShowedModal }) => {
|
||||
const [unitType, setUnitType] = useState('duration'); // kept for potential reuse
|
||||
const [durationUnit, setDurationUnit] = useState('days'); // kept for potential reuse
|
||||
const [availableTypes, setAvailableTypes] = useState([]);
|
||||
const [availableGroups, setAvailableGroups] = useState([]);
|
||||
const [selectedType, setSelectedType] = useState(null);
|
||||
const [selectedGroup, setSelectedGroup] = useState(null);
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
const [products, setProducts] = useState([]);
|
||||
|
||||
const [dashboardData, setDashboardData] = useState({
|
||||
totalRevenue: { amount: 10215845, currency: 'IDR', change: 33.87, period: '22 - 29 May 2025' },
|
||||
totalItemsSold: { amount: 128980, change: -33.87, period: '22 - 29 May 2025' },
|
||||
totalVisitors: { amount: 2905897, change: 33.87, period: '22 - 29 May 2025' },
|
||||
chartData: [
|
||||
{ date: '22/06', items: 200, revenue: 800 },
|
||||
{ date: '23/06', items: 750, revenue: 450 },
|
||||
{ date: '24/06', items: 550, revenue: 200 },
|
||||
{ date: '25/06', items: 300, revenue: 350 },
|
||||
{ date: '26/06', items: 900, revenue: 450 },
|
||||
{ date: '27/06', items: 550, revenue: 200 },
|
||||
],
|
||||
latestTransactions: []
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const fetchDistinctOptions = async () => {
|
||||
const match = document.cookie.match(new RegExp('(^| )token=([^;]+)'));
|
||||
if (!match) return;
|
||||
const token = match[2];
|
||||
|
||||
try {
|
||||
const res = await fetch('https://bot.kediritechnopark.com/webhook/store-production/get-products', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await res.json();
|
||||
const productsArr = result || [];
|
||||
|
||||
const types = [...new Set(productsArr.map(p => p.type).filter(Boolean))];
|
||||
const groups = [...new Set(productsArr.map(p => p.group).filter(Boolean))];
|
||||
|
||||
setAvailableTypes(types);
|
||||
setAvailableGroups(groups);
|
||||
setProducts(processProducts(productsArr));
|
||||
} catch (err) {
|
||||
console.error('Gagal ambil produk:', err);
|
||||
}
|
||||
};
|
||||
|
||||
fetchDistinctOptions();
|
||||
}, []);
|
||||
|
||||
const formatCurrency = (amount) => new Intl.NumberFormat('id-ID').format(amount);
|
||||
|
||||
const getStatusClass = (status) => {
|
||||
switch (status) {
|
||||
case 'confirmed': return styles.statusConfirmed;
|
||||
case 'waiting payment': return styles.statusWaiting;
|
||||
case 'payment expired': return styles.statusExpired;
|
||||
default: return styles.statusConfirmed;
|
||||
}
|
||||
};
|
||||
|
||||
const StatCard = ({ title, value, currency, change, period, icon: Icon, isNegative }) => (
|
||||
<div className={styles.statCard}>
|
||||
<div className={styles.statCardHeader}>
|
||||
<h3 className={styles.statCardTitle}>{title}</h3>
|
||||
<Icon className={styles.statCardIcon} />
|
||||
</div>
|
||||
<div className={styles.statCardValue}>
|
||||
{currency && `${currency} `}{formatCurrency(value)}
|
||||
</div>
|
||||
<div className={styles.statCardFooter}>
|
||||
<div className={styles.statCardChange}>
|
||||
{isNegative ? (
|
||||
<TrendingDown className={`${styles.trendIcon} ${styles.trendDown}`} />
|
||||
) : (
|
||||
<TrendingUp className={`${styles.trendIcon} ${styles.trendUp}`} />
|
||||
)}
|
||||
<span className={`${styles.changeText} ${isNegative ? styles.changeTextNegative : styles.changeTextPositive}`}>
|
||||
{Math.abs(change)}%
|
||||
</span>
|
||||
<span className={styles.fromLastWeek}>from last week</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.statCardPeriod}>{period}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const BarChart = ({ data }) => {
|
||||
const maxValue = Math.max(...data.map(item => Math.max(item.items, item.revenue)));
|
||||
return (
|
||||
<div className={styles.barChart}>
|
||||
{data.map((item, index) => (
|
||||
<div key={index} className={styles.barGroup}>
|
||||
<div className={styles.barContainer}>
|
||||
<div className={`${styles.bar} ${styles.barItems}`} style={{ height: `${(item.items / maxValue) * 200}px` }} />
|
||||
<div className={`${styles.bar} ${styles.barRevenue}`} style={{ height: `${(item.revenue / maxValue) * 200}px` }} />
|
||||
</div>
|
||||
<span className={styles.barLabel}>{item.date}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.statsGrid}>
|
||||
<StatCard title="Total Revenue" value={dashboardData.totalRevenue.amount} currency="IDR" change={dashboardData.totalRevenue.change} period={dashboardData.totalRevenue.period} icon={DollarSign} isNegative={false} />
|
||||
<StatCard title="Total Items Sold" value={dashboardData.totalItemsSold.amount} change={dashboardData.totalItemsSold.change} period={dashboardData.totalItemsSold.period} icon={ShoppingCart} isNegative={true} />
|
||||
<StatCard title="Total Visitor" value={dashboardData.totalVisitors.amount} change={dashboardData.totalVisitors.change} period={dashboardData.totalVisitors.period} icon={Users} isNegative={false} />
|
||||
</div>
|
||||
|
||||
<div className={styles.chartsGrid}>
|
||||
{/* Tempatkan <BarChart data={dashboardData.chartData} /> jika mau ditampilkan */}
|
||||
</div>
|
||||
|
||||
{/* Products List */}
|
||||
<div className={styles.chartCard}>
|
||||
<div className={styles.transactionsHeader}>
|
||||
<h3 className={styles.transactionsTitle}>Products</h3>
|
||||
|
||||
{/* Tombol "Buat Item" → buka modal create */}
|
||||
<button
|
||||
type="button"
|
||||
className={styles.primaryButton}
|
||||
onClick={() => setShowedModal && setShowedModal('create-item')}
|
||||
title="Buat produk baru"
|
||||
>
|
||||
<Plus size={16} style={{ marginRight: 6 }} />
|
||||
Buat Item
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={styles.transactionsList}>
|
||||
{products.map((product) => (
|
||||
<div key={product.id} className={styles.transactionItem}>
|
||||
<div className={styles.transactionLeft}>
|
||||
<div className={styles.transactionInfo}>
|
||||
<h4>{product.name}</h4>
|
||||
{product.children && product.children.map((child) => (
|
||||
<p key={child.id}>- {child.name}</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.transactionRight}>
|
||||
<span className={styles.transactionAmount}>
|
||||
IDR {formatCurrency(product.price)}
|
||||
</span>
|
||||
<div className={`${styles.statusIndicator} ${getStatusClass(product.status)}`}></div>
|
||||
<span className={styles.transactionStatus}>{product.status}</span>
|
||||
|
||||
{/* Tombol "Add Child" → buka modal create dengan parent product_id */}
|
||||
<button
|
||||
type="button"
|
||||
className={styles.secondaryButton}
|
||||
onClick={() => setShowedModal('create-item', product.id)}
|
||||
title="Tambah sub-produk"
|
||||
style={{ marginLeft: '0.75rem' }}
|
||||
>
|
||||
<GitBranchPlus size={16} style={{ marginRight: 6 }} />
|
||||
Add Child
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bagian form create yang lama sudah DIPINDAH ke halaman/komponen baru */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
|
||||
@@ -21,28 +21,41 @@ const Header = ({ username, scrollToProduct, scrollToCourse, setShowedModal, han
|
||||
>
|
||||
HOME
|
||||
</a>
|
||||
<a
|
||||
className={`${styles.navLink} ${hoveredNav === 3 ? styles.navLinkHover : ''}`}
|
||||
onMouseEnter={() => setHoveredNav(3)}
|
||||
onMouseLeave={() => setHoveredNav(null)}
|
||||
onClick={() => {
|
||||
if (!username) scrollToProduct();
|
||||
else navigate('/products');
|
||||
}}
|
||||
>
|
||||
{username ? 'MY PRODUCTS' : 'PRODUCTS'}
|
||||
</a>
|
||||
<a
|
||||
className={`${styles.navLink} ${hoveredNav === 4 ? styles.navLinkHover : ''}`}
|
||||
onMouseEnter={() => setHoveredNav(4)}
|
||||
onMouseLeave={() => setHoveredNav(null)}
|
||||
onClick={() => {
|
||||
if (!username) scrollToCourse();
|
||||
else window.location.href = 'https://academy.kediritechnopark.com'
|
||||
}}
|
||||
>
|
||||
{username ? 'MY ACADEMY' : 'ACADEMY'}
|
||||
</a>
|
||||
{username &&
|
||||
<a
|
||||
className={`${styles.navLink} ${hoveredNav === 3 ? styles.navLinkHover : ''}`}
|
||||
onMouseEnter={() => setHoveredNav(3)}
|
||||
onMouseLeave={() => setHoveredNav(null)}
|
||||
onClick={() => {
|
||||
navigate('/dashboard');
|
||||
}}>
|
||||
DASHBOARD
|
||||
</a>
|
||||
}
|
||||
{!username &&
|
||||
<>
|
||||
<a
|
||||
className={`${styles.navLink} ${hoveredNav === 3 ? styles.navLinkHover : ''}`}
|
||||
onMouseEnter={() => setHoveredNav(3)}
|
||||
onMouseLeave={() => setHoveredNav(null)}
|
||||
onClick={() => {
|
||||
navigate('/products');
|
||||
}}
|
||||
>
|
||||
PRODUCTS
|
||||
</a>
|
||||
<a
|
||||
className={`${styles.navLink} ${hoveredNav === 4 ? styles.navLinkHover : ''}`}
|
||||
onMouseEnter={() => setHoveredNav(4)}
|
||||
onMouseLeave={() => setHoveredNav(null)}
|
||||
onClick={() => {
|
||||
scrollToCourse();
|
||||
}}
|
||||
>
|
||||
ACADEMY
|
||||
</a>
|
||||
</>
|
||||
}
|
||||
</nav>
|
||||
|
||||
{/* Burger Menu Button */}
|
||||
@@ -58,9 +71,9 @@ const Header = ({ username, scrollToProduct, scrollToCourse, setShowedModal, han
|
||||
<div className={styles.username}>{username}</div>
|
||||
|
||||
<button className={styles.logoutButton} onClick={() => {
|
||||
navigate('/products');
|
||||
navigate('/dashboard');
|
||||
}}>
|
||||
MY PRODUCTS
|
||||
DASHBOARD
|
||||
</button>
|
||||
|
||||
<button className={styles.logoutButton} onClick={() => {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const LoginRegister = ({setShowedModal}) => {
|
||||
const navigate = useNavigate();
|
||||
const [tab, setTab] = useState('login'); // 'login' or 'register'
|
||||
const [email, setEmail] = useState('');
|
||||
const [username, setUsername] = useState('');
|
||||
@@ -103,6 +105,7 @@ const LoginRegister = ({setShowedModal}) => {
|
||||
window.history.replaceState({}, '', newUrl);
|
||||
setShowedModal('product');
|
||||
} else {
|
||||
navigate('/dashboard');
|
||||
window.location.reload();
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -4,27 +4,28 @@ import styles from './Styles.module.css';
|
||||
import processProducts from '../helper/processProducts';
|
||||
|
||||
|
||||
const ProductSection = ({ hoveredCard, setHoveredCard, setSelectedProduct, setShowedModal, productSectionRef, setWillDo }) => {
|
||||
const ProductSection = ({ setSelectedProduct, setShowedModal, productSectionRef, setWillDo }) => {
|
||||
const [products, setProducts] = useState([]);
|
||||
// Define this function outside useEffect so it can be called anywhere
|
||||
const [hoveredCard, setHoveredCard] = useState(null);
|
||||
// Define this function outside useEffect so it can be called anywhere
|
||||
|
||||
|
||||
// Inside your component
|
||||
useEffect(() => {
|
||||
fetch('https://bot.kediritechnopark.com/webhook/store-production/products', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ type: 'product' }),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
const enrichedData = processProducts(data);
|
||||
setProducts(enrichedData);
|
||||
// Inside your component
|
||||
useEffect(() => {
|
||||
fetch('https://bot.kediritechnopark.com/webhook/store-production/products', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ type: 'product' }),
|
||||
})
|
||||
.catch(err => console.error('Fetch error:', err));
|
||||
}, []);
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
const enrichedData = processProducts(data);
|
||||
setProducts(enrichedData);
|
||||
})
|
||||
.catch(err => console.error('Fetch error:', err));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -75,12 +76,12 @@ useEffect(() => {
|
||||
: `Rp ${product.price.toLocaleString('id-ID')}`}
|
||||
</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');
|
||||
}}>Beli</button>
|
||||
<button className="px-4 py-2 rounded-pill text-white" style={{ fontSize: '0.8rem', background: 'linear-gradient(to right, #6a59ff, #8261ee)', border: 'none' }}
|
||||
onClick={() => {
|
||||
setSelectedProduct(product);
|
||||
setShowedModal('product');
|
||||
setWillDo('checkout');
|
||||
}}>Beli</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -304,7 +304,7 @@
|
||||
}
|
||||
|
||||
.currentPrice {
|
||||
font-size: 1.2rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
@@ -3,11 +3,8 @@ import ProductDetailPage from '../ProductDetailPage';
|
||||
import Login from '../Login';
|
||||
import styles from '../Styles.module.css';
|
||||
|
||||
const CoursePage = ({ subscriptions }) => {
|
||||
const [postLoginAction, setPostLoginAction] = useState(null);
|
||||
const [selectedProduct, setSelectedProduct] = useState({});
|
||||
const CoursePage = ({ subscriptions, setSelectedProduct, setShowedModal}) => {
|
||||
const [hoveredCard, setHoveredCard] = useState(null);
|
||||
const [showedModal, setShowedModal] = useState(null);
|
||||
const [products, setProducts] = useState([]);
|
||||
|
||||
// Buka modal otomatis berdasarkan query
|
||||
@@ -107,7 +104,7 @@ const CoursePage = ({ subscriptions }) => {
|
||||
children: [],
|
||||
};
|
||||
});
|
||||
console.log(enrichedData)
|
||||
console.log(enrichedData)
|
||||
setProducts(enrichedData);
|
||||
})
|
||||
.catch(err => console.error('Fetch error:', err));
|
||||
@@ -156,50 +153,18 @@ const CoursePage = ({ subscriptions }) => {
|
||||
: `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');
|
||||
}}>Perpanjang</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Features Section */}
|
||||
{/* ... tidak berubah ... */}
|
||||
|
||||
{/* Footer */}
|
||||
{/* ... tidak berubah ... */}
|
||||
|
||||
{/* Unified Modal */}
|
||||
{showedModal && (
|
||||
<div
|
||||
className={styles.modal}
|
||||
onClick={() => {
|
||||
setShowedModal(null);
|
||||
setSelectedProduct({});
|
||||
}}
|
||||
>
|
||||
<div className={styles.modalBody} onClick={(e) => e.stopPropagation()}>
|
||||
{showedModal === 'product' && (
|
||||
<div>
|
||||
<ProductDetailPage
|
||||
setPostLoginAction={setPostLoginAction}
|
||||
setShowedModal={setShowedModal}
|
||||
product={selectedProduct}
|
||||
subscriptions={subscriptions}
|
||||
onClose={() => {
|
||||
setShowedModal(null);
|
||||
setSelectedProduct({});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{showedModal === 'login' && (
|
||||
<Login postLoginAction={postLoginAction} setPostLoginAction={setPostLoginAction} onClose={() => setShowedModal(null)} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div >
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user