Compare commits
7 Commits
b1ae4c5d82
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3776a9559a | ||
|
|
17f5685840 | ||
|
|
c7ab5db1b5 | ||
|
|
fc934c88d8 | ||
|
|
1a84386cdc | ||
|
|
bfbb750c4d | ||
|
|
4ec28f7089 |
BIN
public/assets/cafe-hore.png
Normal file
BIN
public/assets/cafe-hore.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 143 KiB |
BIN
public/assets/kloowear.webp
Normal file
BIN
public/assets/kloowear.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
public/assets/logo_pemprov_jatim.png
Normal file
BIN
public/assets/logo_pemprov_jatim.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 161 KiB |
BIN
public/assets/psi.png
Normal file
BIN
public/assets/psi.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
BIN
public/assets/suar.avif
Normal file
BIN
public/assets/suar.avif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
BIN
public/maya-idle.mp4
Normal file
BIN
public/maya-idle.mp4
Normal file
Binary file not shown.
48
src/App.js
48
src/App.js
@@ -38,11 +38,11 @@ function HomePage({
|
||||
|
||||
if (tab === 'products') scrollToProduct();
|
||||
if (tab === 'academy') scrollToCourse();
|
||||
}, [productSectionRef, courseSectionRef]);
|
||||
}, [productSectionRef.current, courseSectionRef.current]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeroSection />
|
||||
<HeroSection scrollToProduct={scrollToProduct} scrollToCourse={scrollToCourse}/>
|
||||
|
||||
<AboutUsSection />
|
||||
<ServicesSection />
|
||||
@@ -159,7 +159,7 @@ function App() {
|
||||
if (unauthorizedUri) localStorage.setItem('unauthorized_uri', unauthorizedUri);
|
||||
|
||||
// Jika belum login, tampilkan modal login
|
||||
if (!token) {
|
||||
if (!token && authorizedUri) {
|
||||
setShowedModal('login');
|
||||
}
|
||||
// Jika sudah login, tidak langsung fetch di sini — akan diproses saat subscriptions tersedia
|
||||
@@ -179,7 +179,7 @@ function App() {
|
||||
const token = document.cookie.match(/(^| )token=([^;]+)/)?.[2];
|
||||
|
||||
if (modalType === 'product' && productId) {
|
||||
if (!token) {
|
||||
if (!token && authorizedUri) {
|
||||
setShowedModal('login'); // belum login → tampilkan login modal
|
||||
} else {
|
||||
// sudah login → lanjutkan proses otorisasi saat subscriptions tersedia
|
||||
@@ -194,31 +194,42 @@ function App() {
|
||||
if (!productModalRequest || !subscriptions) return;
|
||||
|
||||
const { productId, authorizedUri, unauthorizedUri } = productModalRequest;
|
||||
console.log(subscriptions)
|
||||
|
||||
const hasAccess = subscriptions && subscriptions.some(
|
||||
sub => sub.product_id === productId || sub.product_parent_id === productId
|
||||
);
|
||||
console.log(hasAccess)
|
||||
console.log("hasAccess:", hasAccess);
|
||||
|
||||
if (hasAccess) {
|
||||
if (authorizedUri) {
|
||||
let finalUri = decodeURIComponent(authorizedUri);
|
||||
const token = document.cookie.match(/(^| )token=([^;]+)/)?.[2];
|
||||
|
||||
if (finalUri.includes('token=null') || finalUri.includes('token=')) {
|
||||
// --- ambil product_name distinct berdasarkan productId/parent ---
|
||||
const relatedSubs = subscriptions.filter(
|
||||
sub => sub.product_id === productId || sub.product_parent_id === productId
|
||||
);
|
||||
const distinctNames = [...new Set(relatedSubs.map(sub => sub.product_name))];
|
||||
|
||||
if (distinctNames.length > 1) {
|
||||
// lebih dari 1 → pakai dashboard=true
|
||||
const url = new URL(finalUri);
|
||||
url.searchParams.set('token', token || '');
|
||||
url.searchParams.set("token", token || "");
|
||||
finalUri = url.toString();
|
||||
} else if (distinctNames.length === 1) {
|
||||
// hanya 1 → tambahkan productName=<nama> sebelum query lain
|
||||
const url = new URL(finalUri);
|
||||
url.searchParams.set("token", token || "");
|
||||
url.searchParams.set("productName", distinctNames[0]);
|
||||
finalUri = url.toString();
|
||||
}
|
||||
|
||||
window.location.href = finalUri;
|
||||
}
|
||||
else {// Assuming you already imported processProducts from './processProducts'
|
||||
|
||||
} else {
|
||||
// fallback ambil detail produk via fetch
|
||||
fetch('https://bot.kediritechnopark.com/webhook/store-production/products', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
itemsId: [productId],
|
||||
withChildren: true,
|
||||
@@ -227,9 +238,7 @@ function App() {
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
// Process the raw data to group children under their parent
|
||||
const processed = processProducts(data);
|
||||
// Set the first product (which should be the parent with children nested)
|
||||
setSelectedProduct(processed[0]);
|
||||
setShowedModal('product');
|
||||
}
|
||||
@@ -240,12 +249,9 @@ function App() {
|
||||
if (unauthorizedUri) {
|
||||
window.location.href = decodeURIComponent(unauthorizedUri);
|
||||
} else {
|
||||
|
||||
fetch('https://bot.kediritechnopark.com/webhook/store-production/products', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
itemsId: [productId],
|
||||
withChildren: true,
|
||||
@@ -254,9 +260,7 @@ function App() {
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
// Process the raw data to group children under their parent
|
||||
const processed = processProducts(data);
|
||||
// Set the first product (which should be the parent with children nested)
|
||||
setSelectedProduct(processed[0]);
|
||||
setShowedModal('product');
|
||||
}
|
||||
|
||||
@@ -65,10 +65,7 @@ const AcademySection = ({setSelectedProduct, setShowedModal, courseSectionRef, s
|
||||
<div
|
||||
key={product.id}
|
||||
className={`${styles.courseCard} ${hoveredCard === product.id ? styles.courseCardHover : ''}`}
|
||||
onClick={() => {
|
||||
setSelectedProduct(product);
|
||||
setShowedModal('product');
|
||||
}}
|
||||
|
||||
onMouseEnter={() => setHoveredCard(product.id)}
|
||||
onMouseLeave={() => setHoveredCard(null)}
|
||||
>
|
||||
|
||||
@@ -5,10 +5,11 @@ import useInView from '../hooks/useInView';
|
||||
|
||||
const ClientsSection = () => {
|
||||
const logos = [
|
||||
'dermalounge.jpg',
|
||||
'logo_pemprov_jatim.png',
|
||||
'suar.avif',
|
||||
'kloowear.png',
|
||||
'kloowear.webp',
|
||||
'psi.png',
|
||||
'cafe-hore.png'
|
||||
];
|
||||
|
||||
const { ref, inView } = useInView();
|
||||
@@ -25,7 +26,7 @@ const ClientsSection = () => {
|
||||
{logos.map((logo, index) => (
|
||||
<div className={`${styles.clientLogoWrapper} m-2`} key={index}>
|
||||
<Image
|
||||
src={`https://kediritechnopark.com/assets/${logo}`}
|
||||
src={`/assets/${logo}`}
|
||||
fluid
|
||||
className={styles.clientLogo}
|
||||
/>
|
||||
|
||||
@@ -1,51 +1,79 @@
|
||||
// HeroSection.jsx — 2025 refresh using React-Bootstrap + CSS Module
|
||||
import React from 'react';
|
||||
import { Container, Row, Col, Button } from 'react-bootstrap';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import styles from './HeroSection.module.css';
|
||||
|
||||
const HeroSection = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const goProducts = () => navigate('/products');
|
||||
const goAcademy = () => navigate('/#services');
|
||||
import { Container, Row, Col, Button } from "react-bootstrap";
|
||||
import styles from "./HeroSection.module.css";
|
||||
|
||||
const HeroSection = ({ scrollToProduct, scrollToCourse }) => {
|
||||
return (
|
||||
<section className={`${styles.hero} pt-3 pb-3`}
|
||||
aria-label="Kediri Technopark hero section">
|
||||
<section
|
||||
className={`${styles.hero} pt-3`}
|
||||
aria-label="Kediri Technopark hero section"
|
||||
>
|
||||
<Container className={styles.heroContainer}>
|
||||
<Row className="align-items-center gy-3">
|
||||
{/* Text first for mobile and desktop for clarity */}
|
||||
<Col xs={{ order: 0 }} lg={{ span: 8, order: 1 }} xl={{ span: 7, order: 1 }}>
|
||||
<Col
|
||||
xs={{ order: 0 }}
|
||||
lg={{ span: 8, order: 1 }}
|
||||
xl={{ span: 7, order: 1 }}
|
||||
>
|
||||
<div className={styles.copyWrap}>
|
||||
<h1 className={styles.title}>
|
||||
KATALIS KARIR DAN BISNIS DIGITAL
|
||||
</h1>
|
||||
<h1 className={styles.title}>KATALIS KARIR DAN BISNIS DIGITAL</h1>
|
||||
<p className={styles.lead}>
|
||||
Kami adalah ekosistem tempat mimpi digital tumbuh dan masa depan dibentuk. Di sinilah semangat belajar bertemu dengan inovasi, dan ide-ide muda diberi ruang untuk berkembang. Lebih dari sekadar tempat, kami adalah rumah bagi talenta, teknologi, dan transformasi. Mari jelajahi dunia digital, bangun karir, dan ciptakan solusi — semua dimulai dari sini.
|
||||
Kami adalah ekosistem tempat mimpi digital tumbuh dan masa depan
|
||||
dibentuk. Di sinilah semangat belajar bertemu dengan inovasi,
|
||||
dan ide-ide muda diberi ruang untuk berkembang. Lebih dari
|
||||
sekadar tempat, kami adalah rumah bagi talenta, teknologi, dan
|
||||
transformasi. Mari jelajahi dunia digital, bangun karir, dan
|
||||
ciptakan solusi — semua dimulai dari sini.
|
||||
</p>
|
||||
<div className={styles.ctaGroup}>
|
||||
<Button className={styles.ctaPrimary} onClick={goProducts}>
|
||||
<Button className={styles.ctaPrimary} onClick={scrollToProduct}>
|
||||
Explore Products
|
||||
</Button>
|
||||
<Button variant="light" className={styles.ctaSecondary} onClick={goAcademy}>
|
||||
<Button
|
||||
variant="light"
|
||||
className={styles.ctaSecondary}
|
||||
onClick={scrollToCourse}
|
||||
>
|
||||
View Academy
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col xs={{ order: 1 }} lg={{ span: 4, order: 2 }} xl={{ span: 5, order: 2 }}>
|
||||
<Col
|
||||
xs={{ order: 1 }}
|
||||
lg={{ span: 4, order: 2 }}
|
||||
xl={{ span: 5, order: 2 }}
|
||||
>
|
||||
<div className={styles.imageWrap}>
|
||||
<div className={styles.imageFrame}>
|
||||
<img
|
||||
src="https://kediritechnopark.com/assets/hero.png"
|
||||
alt="Ekosistem digital Kediri Technopark"
|
||||
<video
|
||||
className={`img-fluid ${styles.heroImage}`}
|
||||
autoPlay
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
>
|
||||
<source src="/maya-idle.mp4" type="video/mp4" />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.header}>
|
||||
<h1>Perkenalkan Maya</h1>
|
||||
<p>Asisten digital kami</p>
|
||||
<Button
|
||||
variant="light"
|
||||
className={styles.ctaSecondary}
|
||||
onClick={() =>
|
||||
(window.location.href =
|
||||
"https://mayagen-cs.kediritechnopark.com")
|
||||
}
|
||||
>
|
||||
Bincang dengan Maya
|
||||
</Button>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
.hero {
|
||||
position: relative;
|
||||
background:
|
||||
radial-gradient(900px 400px at 0% -10%, color-mix(in srgb, var(--brand) 12%, transparent), transparent 60%),
|
||||
radial-gradient(800px 350px at 110% 0%, rgba(0,0,0,0.05), transparent 60%);
|
||||
background: radial-gradient(
|
||||
900px 400px at 0% -10%,
|
||||
color-mix(in srgb, var(--brand) 12%, transparent),
|
||||
transparent 60%
|
||||
),
|
||||
radial-gradient(
|
||||
800px 350px at 110% 0%,
|
||||
rgba(0, 0, 0, 0.05),
|
||||
transparent 60%
|
||||
);
|
||||
overflow: visible;
|
||||
min-height: clamp(300px, 40svh, 450px);
|
||||
display: grid;
|
||||
@@ -25,7 +32,9 @@
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.kickerRow { margin-bottom: .25rem; }
|
||||
.kickerRow {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.kickerBadge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -45,7 +54,11 @@
|
||||
letter-spacing: -0.02em;
|
||||
/* Fluid type: min 24px → max 40px, ensure 1-line on desktop */
|
||||
font-size: clamp(1.8rem, 2vw + 0.8rem, 2.8rem);
|
||||
background: linear-gradient(92deg, var(--text) 0%, color-mix(in srgb, var(--brand) 70%, #0f172a) 100%);
|
||||
background: linear-gradient(
|
||||
92deg,
|
||||
var(--text) 0%,
|
||||
color-mix(in srgb, var(--brand) 70%, #0f172a) 100%
|
||||
);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
@@ -76,7 +89,8 @@
|
||||
}
|
||||
|
||||
.bulletIcon {
|
||||
width: 18px; height: 18px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--brand);
|
||||
box-shadow: inset 0 0 0 2px #fff;
|
||||
@@ -96,8 +110,8 @@
|
||||
padding: 0.45rem 0.8rem !important;
|
||||
font-weight: 600 !important;
|
||||
letter-spacing: 0.02em;
|
||||
transition: background-color .16s ease, border-color .16s ease;
|
||||
margin-right: .5rem;
|
||||
transition: background-color 0.16s ease, border-color 0.16s ease;
|
||||
margin-right: 0.5rem;
|
||||
box-shadow: var(--shadow-neutral-s);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
@@ -117,7 +131,8 @@
|
||||
padding: 0.45rem 0.8rem !important;
|
||||
font-weight: 600 !important;
|
||||
letter-spacing: 0.02em;
|
||||
transition: color .16s ease, border-color .16s ease, background-color .16s ease;
|
||||
transition: color 0.16s ease, border-color 0.16s ease,
|
||||
background-color 0.16s ease;
|
||||
box-shadow: var(--shadow-neutral-s);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
@@ -150,14 +165,29 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.imageWrap::before, .imageWrap::after { content: none; }
|
||||
.imageWrap::before,
|
||||
.imageWrap::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.imageFrame {
|
||||
position: relative;
|
||||
border-radius: calc(var(--radius-2xl) + 6px);
|
||||
overflow: hidden;
|
||||
mask-image: linear-gradient(to right, transparent 0%, black 10%, black 90%, transparent 100%);
|
||||
-webkit-mask-image: linear-gradient(to right, transparent 0%, black 10%, black 90%, transparent 100%);
|
||||
mask-image: linear-gradient(
|
||||
to right,
|
||||
transparent 0%,
|
||||
black 10%,
|
||||
black 90%,
|
||||
transparent 100%
|
||||
);
|
||||
-webkit-mask-image: linear-gradient(
|
||||
to right,
|
||||
transparent 0%,
|
||||
black 10%,
|
||||
black 90%,
|
||||
transparent 100%
|
||||
);
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -166,9 +196,141 @@
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
aspect-ratio: 4 / 3;
|
||||
object-fit: cover;
|
||||
border-radius: calc(var(--radius-2xl) - 4px);
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: absolute;
|
||||
top: 233px;
|
||||
right: 150px;
|
||||
z-index: 10;
|
||||
max-width: 450px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 3.02rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.75rem;
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.01em;
|
||||
white-space: nowrap;
|
||||
background: linear-gradient(
|
||||
120deg,
|
||||
#0f172a 0%,
|
||||
#1e3a8a 25%,
|
||||
#2563eb 50%,
|
||||
#1e40af 75%,
|
||||
#0f172a 100%
|
||||
);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
background-size: 200% auto;
|
||||
animation: gradientFlow 5s ease-in-out infinite;
|
||||
filter: drop-shadow(2px 2px 4px rgba(255, 255, 255, 0.8))
|
||||
drop-shadow(-1px -1px 2px rgba(255, 255, 255, 0.6))
|
||||
drop-shadow(0 3px 10px rgba(37, 99, 235, 0.2));
|
||||
}
|
||||
|
||||
@keyframes gradientFlow {
|
||||
0%,
|
||||
100% {
|
||||
background-position: 0% center;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% center;
|
||||
}
|
||||
}
|
||||
|
||||
.header p {
|
||||
font-size: 1.125rem;
|
||||
color: #475569;
|
||||
margin-bottom: 1.5rem;
|
||||
font-weight: 500;
|
||||
text-shadow: 1px 1px 3px rgba(255, 255, 255, 0.9),
|
||||
0 2px 6px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.ctaSecondary {
|
||||
background: rgba(255, 255, 255, 0.75);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border: 2.5px solid #1e40af;
|
||||
color: #1e40af;
|
||||
padding: 0.875rem 2rem;
|
||||
border-radius: 28px;
|
||||
font-weight: 600;
|
||||
font-size: 0.975rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 3px 10px rgba(30, 64, 175, 0.15),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ctaSecondary:hover {
|
||||
background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 50%, #2563eb 100%);
|
||||
color: white;
|
||||
border-color: #1e3a8a;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(30, 58, 138, 0.4),
|
||||
0 2px 8px rgba(37, 99, 235, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.ctaSecondary:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 1400px) {
|
||||
.header {
|
||||
right: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.header {
|
||||
right: 80px;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.header {
|
||||
position: static;
|
||||
max-width: 100%;
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.5rem;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.header p {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.ctaSecondary {
|
||||
padding: 0.75rem 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.glow {
|
||||
@@ -177,7 +339,11 @@
|
||||
height: 40%;
|
||||
filter: blur(40px);
|
||||
z-index: -1;
|
||||
background: radial-gradient(60% 60% at 50% 0%, color-mix(in srgb, var(--brand) 30%, transparent), transparent 60%);
|
||||
background: radial-gradient(
|
||||
60% 60% at 50% 0%,
|
||||
color-mix(in srgb, var(--brand) 30%, transparent),
|
||||
transparent 60%
|
||||
);
|
||||
}
|
||||
|
||||
.stats {
|
||||
@@ -185,54 +351,117 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: #64748b; /* slate-500 */
|
||||
color: #64748b;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.statItem strong { color: #0f172a; font-weight: 700; margin-right: 4px; }
|
||||
.statDot { width: 4px; height: 4px; border-radius: 2px; background: #cbd5e1; }
|
||||
.statItem strong {
|
||||
color: #0f172a;
|
||||
font-weight: 700;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.statDot {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
border-radius: 2px;
|
||||
background: #cbd5e1;
|
||||
}
|
||||
|
||||
/* Fine-tuned responsive spacing */
|
||||
@media (min-width: 992px) {
|
||||
.copyWrap { padding-right: 1rem; }
|
||||
.copyWrap {
|
||||
padding-right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575.98px) {
|
||||
.hero { padding-top: 1rem; }
|
||||
.ctaGroup { display: grid; gap: 8px; }
|
||||
.ctaPrimary, .ctaSecondary { width: 100% !important; text-align: center; }
|
||||
.copyWrap { max-width: 100%; padding: 0 10px; }
|
||||
.title { font-size: clamp(1.3rem, 2.5vw + 1rem, 1.8rem); }
|
||||
.lead { font-size: clamp(0.9rem, 0.5vw + 0.8rem, 1rem); }
|
||||
.hero {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
.ctaGroup {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
.ctaPrimary,
|
||||
.ctaSecondary {
|
||||
width: 100% !important;
|
||||
text-align: center;
|
||||
}
|
||||
.copyWrap {
|
||||
max-width: 100%;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.title {
|
||||
font-size: clamp(1.3rem, 2.5vw + 1rem, 1.8rem);
|
||||
}
|
||||
.lead {
|
||||
font-size: clamp(0.9rem, 0.5vw + 0.8rem, 1rem);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.imageWrap::before,
|
||||
.imageWrap::after { display: none; }
|
||||
.title { font-size: clamp(1.4rem, 2vw + 1rem, 2.1rem); line-height: 1.12; }
|
||||
.lead { font-size: clamp(0.93rem, 0.4vw + 0.84rem, 1.03rem); }
|
||||
.bulletItem { font-size: 0.92rem; }
|
||||
.mesh, .grid { display: none; }
|
||||
.copyWrap { max-width: 100%; padding: 0 15px; }
|
||||
.imageWrap { max-width: 100%; }
|
||||
.imageFrame { border-radius: calc(var(--radius-2xl) + 2px); }
|
||||
.heroImage { border-radius: calc(var(--radius-2xl) - 6px); }
|
||||
.imageWrap::after {
|
||||
display: none;
|
||||
}
|
||||
.title {
|
||||
font-size: clamp(1.4rem, 2vw + 1rem, 2.1rem);
|
||||
line-height: 1.12;
|
||||
}
|
||||
.lead {
|
||||
font-size: clamp(0.93rem, 0.4vw + 0.84rem, 1.03rem);
|
||||
}
|
||||
.bulletItem {
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
.mesh,
|
||||
.grid {
|
||||
display: none;
|
||||
}
|
||||
.copyWrap {
|
||||
max-width: 100%;
|
||||
padding: 0 15px;
|
||||
}
|
||||
.imageWrap {
|
||||
max-width: 100%;
|
||||
}
|
||||
.imageFrame {
|
||||
border-radius: calc(var(--radius-2xl) + 2px);
|
||||
}
|
||||
.heroImage {
|
||||
border-radius: calc(var(--radius-2xl) - 6px);
|
||||
}
|
||||
}
|
||||
|
||||
.imageFrame:hover { box-shadow: none; transform: none; }
|
||||
.imageFrame:hover {
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
@media (min-width: 1400px) {
|
||||
.imageWrap { max-width: 720px; }
|
||||
.imageWrap {
|
||||
max-width: 720px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.hero { padding-top: 0.8rem; }
|
||||
.title { font-size: clamp(1.2rem, 3vw + 0.9rem, 1.7rem); }
|
||||
.lead { font-size: clamp(0.85rem, 0.5vw + 0.75rem, 0.95rem); }
|
||||
.ctaGroup { gap: 6px; }
|
||||
.ctaPrimary, .ctaSecondary {
|
||||
.hero {
|
||||
padding-top: 0.8rem;
|
||||
}
|
||||
.title {
|
||||
font-size: clamp(1.2rem, 3vw + 0.9rem, 1.7rem);
|
||||
}
|
||||
.lead {
|
||||
font-size: clamp(0.85rem, 0.5vw + 0.75rem, 0.95rem);
|
||||
}
|
||||
.ctaGroup {
|
||||
gap: 6px;
|
||||
}
|
||||
.ctaPrimary,
|
||||
.ctaSecondary {
|
||||
padding: 0.4rem 0.7rem !important;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.copyWrap { padding: 0 5px; }
|
||||
.copyWrap {
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
@@ -37,12 +37,12 @@ const ProductCard = ({ product, onCardClick, isCenter, canHover, onCollapse }) =
|
||||
>
|
||||
Detail
|
||||
</button>
|
||||
<button
|
||||
{/* <button
|
||||
className={styles.buyButton}
|
||||
onClick={(e) => { e.preventDefault(); e.stopPropagation(); onCardClick && onCardClick(product); }}
|
||||
>
|
||||
Beli
|
||||
</button>
|
||||
</button> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.45s ease, filter 0.45s ease;
|
||||
background-color: #f1f1f1;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.canHover:hover .cover {
|
||||
|
||||
@@ -38,13 +38,36 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
||||
}
|
||||
// Auto check saat user mengetik (debounce)
|
||||
useEffect(() => {
|
||||
if (product.unique_name == false) return;
|
||||
|
||||
const name = customName.trim();
|
||||
|
||||
if (!name) {
|
||||
setStatus('idle');
|
||||
return;
|
||||
}
|
||||
|
||||
const isNameTaken = subscriptions?.some(sub => {
|
||||
const isSameProduct = sub.product_id === product.id || sub.product_parent_id === product.id;
|
||||
if (!isSameProduct) return false;
|
||||
|
||||
const existingName = sub.product_name?.split('%%%')[0]?.trim().toLowerCase();
|
||||
return existingName === name.toLowerCase();
|
||||
});
|
||||
|
||||
|
||||
if (subscriptions && isNameTaken) {
|
||||
setStatus('unavailable');
|
||||
return;
|
||||
}
|
||||
else if(!product.unique_name){
|
||||
setStatus('available');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!product.unique_name) {
|
||||
console.log(subscriptions);
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
setStatus('checking');
|
||||
|
||||
@@ -79,7 +102,7 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
||||
subscriptions.some(sub =>
|
||||
String(sub.product_id) === String(product.id) || String(sub.product_parent_id) === String(product.id)
|
||||
);
|
||||
|
||||
console.log(hasMatchingSubscription)
|
||||
// ✅ Check subscription first
|
||||
if (hasMatchingSubscription) {
|
||||
const matching = subscriptions.filter(sub =>
|
||||
@@ -96,7 +119,7 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
||||
return;
|
||||
} else {
|
||||
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/dashboard&redirect_failed=https://kediritechnopark.com`;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -108,7 +131,7 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
||||
|
||||
// Fallback: direct checkout
|
||||
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/dashboard&redirect_failed=https://kediritechnopark.com`;
|
||||
};
|
||||
|
||||
// ✅ Confirm child selection (final step after naming)
|
||||
@@ -124,7 +147,7 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
||||
const encodedName = encodeURIComponent(customName.trim() || product.name);
|
||||
const itemsParam = JSON.stringify(selectedChildIds);
|
||||
|
||||
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/dashboard&redirect_failed=https://kediritechnopark.com`;
|
||||
};
|
||||
|
||||
// ✅ User sets name first → then if product has children, show child selector
|
||||
@@ -149,7 +172,7 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
||||
const itemsParam = JSON.stringify([product.id]);
|
||||
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/dashboard&redirect_failed=https://kediritechnopark.com`;
|
||||
};
|
||||
|
||||
const onConfirmSelector = () => {
|
||||
@@ -171,14 +194,26 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
||||
const productName = selectedSubscription?.product_name;
|
||||
const encodedName = encodeURIComponent(productName);
|
||||
|
||||
window.location.href = `https://checkout.kediritechnopark.com/?token=${token}&itemsId=${itemsParam}&set_name=${encodedName}&redirect_uri=https://kediritechnopark.com/products&redirect_failed=https://kediritechnopark.com`;
|
||||
window.location.href = `https://checkout.kediritechnopark.com/?token=${token}&itemsId=${itemsParam}&set_name=${encodedName}&redirect_uri=https://kediritechnopark.com/dashboard&redirect_failed=https://kediritechnopark.com`;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (willDo === 'checkout') {
|
||||
if (!product.executeCheckout && willDo === 'checkout') {
|
||||
onCheckout();
|
||||
}
|
||||
else if (product.children && product.children.length > 0) {
|
||||
setShowChildSelector(true);
|
||||
}
|
||||
else {
|
||||
|
||||
const tokenCookie = document.cookie.split('; ').find(row => row.startsWith('token='));
|
||||
const token = tokenCookie ? tokenCookie.split('=')[1] : '';
|
||||
const encodedName = encodeURIComponent(product.name);
|
||||
const itemsParam = JSON.stringify([product.id]);
|
||||
|
||||
window.location.href = `https://checkout.kediritechnopark.com/?token=${token}&itemsId=${itemsParam}&set_name=${encodedName}&redirect_uri=https://kediritechnopark.com/dashboard&redirect_failed=https://kediritechnopark.com`;
|
||||
}
|
||||
if (setWillDo) setWillDo('');
|
||||
}, []);
|
||||
|
||||
@@ -318,7 +353,7 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
||||
style={{ width: '100%', padding: '8px', marginBottom: '8px', borderRadius: '10px' }}
|
||||
/>
|
||||
|
||||
{product.unique_name && <StatusLine />}
|
||||
<StatusLine />
|
||||
|
||||
<div className={styles.buttonGroup} style={{ marginTop: 12 }}>
|
||||
<button className={styles.button} onClick={() => setShowNamingInput(false)}>
|
||||
|
||||
@@ -42,9 +42,9 @@ const ProductSection = ({ setSelectedProduct, setShowedModal, productSectionRef,
|
||||
|
||||
// Handle product selection for detail view
|
||||
const handleViewDetail = (product, detailed) => {
|
||||
console.log(product, detailed)
|
||||
if(detailed) {
|
||||
setSelectedProduct(product);
|
||||
setShowedModal('product');
|
||||
window.location.href = product.site_landing_url;
|
||||
return;
|
||||
}
|
||||
setSelectedProduct(product);
|
||||
|
||||
@@ -1,499 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const KedaiMasterLanding = () => {
|
||||
const styles = {
|
||||
container: {
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
background: 'linear-gradient(135deg, #e8f5e8 0%, #f0f8ff 100%)',
|
||||
minHeight: '100vh'
|
||||
},
|
||||
header: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
padding: '1rem 2rem',
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||
backdropFilter: 'blur(10px)'
|
||||
},
|
||||
logo: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '0.5rem',
|
||||
fontSize: '1.2rem',
|
||||
fontWeight: 'bold',
|
||||
color: '#2c3e50'
|
||||
},
|
||||
nav: {
|
||||
display: 'flex',
|
||||
gap: '2rem',
|
||||
listStyle: 'none',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
},
|
||||
navLink: {
|
||||
textDecoration: 'none',
|
||||
color: '#2c3e50',
|
||||
fontSize: '0.9rem',
|
||||
transition: 'color 0.3s'
|
||||
},
|
||||
ctaButton: {
|
||||
backgroundColor: '#4a90e2',
|
||||
color: 'white',
|
||||
padding: '0.5rem 1.5rem',
|
||||
border: 'none',
|
||||
borderRadius: '25px',
|
||||
fontSize: '0.9rem',
|
||||
cursor: 'pointer',
|
||||
transition: 'transform 0.3s'
|
||||
},
|
||||
hero: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
padding: '4rem 2rem',
|
||||
maxWidth: '1200px',
|
||||
margin: '0 auto'
|
||||
},
|
||||
heroContent: {
|
||||
flex: 1,
|
||||
paddingRight: '2rem'
|
||||
},
|
||||
heroTitle: {
|
||||
fontSize: '3rem',
|
||||
fontWeight: 'bold',
|
||||
color: '#2c3e50',
|
||||
marginBottom: '1rem',
|
||||
lineHeight: '1.2'
|
||||
},
|
||||
heroText: {
|
||||
fontSize: '1.1rem',
|
||||
color: '#666',
|
||||
lineHeight: '1.6',
|
||||
marginBottom: '2rem'
|
||||
},
|
||||
heroImage: {
|
||||
flex: 1,
|
||||
textAlign: 'center'
|
||||
},
|
||||
coffeeIcon: {
|
||||
fontSize: '8rem',
|
||||
background: 'linear-gradient(45deg, #ffa726, #ff9800)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
padding: '2rem',
|
||||
borderRadius: '20px',
|
||||
backgroundColor: 'rgba(255, 167, 38, 0.1)'
|
||||
},
|
||||
features: {
|
||||
padding: '4rem 2rem',
|
||||
maxWidth: '1200px',
|
||||
margin: '0 auto'
|
||||
},
|
||||
featuresTitle: {
|
||||
textAlign: 'center',
|
||||
fontSize: '2.5rem',
|
||||
color: '#2c3e50',
|
||||
marginBottom: '3rem'
|
||||
},
|
||||
featuresGrid: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))',
|
||||
gap: '2rem',
|
||||
marginBottom: '4rem'
|
||||
},
|
||||
featureCard: {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.3)',
|
||||
padding: '2rem',
|
||||
borderRadius: '15px',
|
||||
textAlign: 'center',
|
||||
backdropFilter: 'blur(10px)',
|
||||
border: '1px solid rgba(255, 255, 255, 0.2)',
|
||||
transition: 'transform 0.3s'
|
||||
},
|
||||
featureTitle: {
|
||||
fontSize: '1.3rem',
|
||||
fontWeight: 'bold',
|
||||
color: '#2c3e50',
|
||||
marginBottom: '1rem'
|
||||
},
|
||||
featureText: {
|
||||
color: '#666',
|
||||
lineHeight: '1.5'
|
||||
},
|
||||
appShowcase: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '3rem',
|
||||
marginTop: '4rem'
|
||||
},
|
||||
appContent: {
|
||||
flex: 1
|
||||
},
|
||||
appTitle: {
|
||||
fontSize: '1.5rem',
|
||||
fontWeight: 'bold',
|
||||
color: '#2c3e50',
|
||||
marginBottom: '1rem'
|
||||
},
|
||||
appText: {
|
||||
color: '#666',
|
||||
lineHeight: '1.6'
|
||||
},
|
||||
appImages: {
|
||||
flex: 1,
|
||||
position: 'relative',
|
||||
height: '300px'
|
||||
},
|
||||
phoneScreen: {
|
||||
width: '200px',
|
||||
height: '350px',
|
||||
backgroundColor: 'white',
|
||||
borderRadius: '25px',
|
||||
padding: '1rem',
|
||||
boxShadow: '0 10px 30px rgba(0,0,0,0.2)',
|
||||
position: 'absolute',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '0.5rem'
|
||||
},
|
||||
phoneScreen1: {
|
||||
left: '0',
|
||||
top: '0',
|
||||
zIndex: 2
|
||||
},
|
||||
phoneScreen2: {
|
||||
right: '0',
|
||||
top: '50px',
|
||||
zIndex: 1
|
||||
},
|
||||
screenHeader: {
|
||||
height: '40px',
|
||||
backgroundColor: '#f0f0f0',
|
||||
borderRadius: '10px',
|
||||
marginBottom: '0.5rem'
|
||||
},
|
||||
screenContent: {
|
||||
flex: 1,
|
||||
backgroundColor: '#f8f8f8',
|
||||
borderRadius: '10px',
|
||||
padding: '0.5rem'
|
||||
},
|
||||
cta: {
|
||||
textAlign: 'center',
|
||||
padding: '4rem 2rem',
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.2)',
|
||||
backdropFilter: 'blur(10px)'
|
||||
},
|
||||
ctaTitle: {
|
||||
fontSize: '2rem',
|
||||
color: '#2c3e50',
|
||||
marginBottom: '2rem'
|
||||
},
|
||||
ctaButtonLarge: {
|
||||
backgroundColor: '#8b4513',
|
||||
color: 'white',
|
||||
padding: '1rem 2rem',
|
||||
border: 'none',
|
||||
borderRadius: '30px',
|
||||
fontSize: '1.1rem',
|
||||
cursor: 'pointer',
|
||||
transition: 'transform 0.3s'
|
||||
},
|
||||
pricing: {
|
||||
padding: '4rem 2rem',
|
||||
background: 'linear-gradient(135deg, #ffa726 0%, #ff9800 100%)',
|
||||
textAlign: 'center'
|
||||
},
|
||||
pricingTitle: {
|
||||
fontSize: '2.5rem',
|
||||
color: 'white',
|
||||
marginBottom: '3rem'
|
||||
},
|
||||
pricingCards: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
|
||||
gap: '2rem',
|
||||
maxWidth: '1000px',
|
||||
margin: '0 auto'
|
||||
},
|
||||
pricingCard: {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.95)',
|
||||
padding: '2rem',
|
||||
borderRadius: '15px',
|
||||
position: 'relative'
|
||||
},
|
||||
pricingBadge: {
|
||||
position: 'absolute',
|
||||
top: '-10px',
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
backgroundColor: '#4caf50',
|
||||
color: 'white',
|
||||
padding: '0.5rem 1rem',
|
||||
borderRadius: '20px',
|
||||
fontSize: '0.8rem'
|
||||
},
|
||||
pricingPlan: {
|
||||
fontSize: '1.3rem',
|
||||
fontWeight: 'bold',
|
||||
color: '#2c3e50',
|
||||
marginBottom: '1rem'
|
||||
},
|
||||
pricingPrice: {
|
||||
fontSize: '2rem',
|
||||
fontWeight: 'bold',
|
||||
color: '#4caf50',
|
||||
marginBottom: '1.5rem'
|
||||
},
|
||||
pricingFeatures: {
|
||||
listStyle: 'none',
|
||||
padding: 0,
|
||||
marginBottom: '2rem'
|
||||
},
|
||||
pricingFeature: {
|
||||
padding: '0.5rem 0',
|
||||
color: '#666',
|
||||
borderBottom: '1px solid #eee'
|
||||
},
|
||||
pricingButton: {
|
||||
backgroundColor: '#4caf50',
|
||||
color: 'white',
|
||||
padding: '0.8rem 2rem',
|
||||
border: 'none',
|
||||
borderRadius: '25px',
|
||||
cursor: 'pointer',
|
||||
width: '100%',
|
||||
fontSize: '1rem'
|
||||
},
|
||||
footer: {
|
||||
background: 'linear-gradient(135deg, #2196f3 0%, #21cbf3 100%)',
|
||||
color: 'white',
|
||||
padding: '4rem 2rem 2rem',
|
||||
position: 'relative',
|
||||
overflow: 'hidden'
|
||||
},
|
||||
footerWave: {
|
||||
position: 'absolute',
|
||||
top: '-50px',
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100px',
|
||||
background: 'rgba(255, 255, 255, 0.1)',
|
||||
borderRadius: '50% 50% 0 0'
|
||||
},
|
||||
footerContent: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
|
||||
gap: '3rem',
|
||||
maxWidth: '1200px',
|
||||
margin: '0 auto',
|
||||
position: 'relative',
|
||||
zIndex: 1
|
||||
},
|
||||
footerSection: {
|
||||
textAlign: 'left'
|
||||
},
|
||||
footerTitle: {
|
||||
fontSize: '1.5rem',
|
||||
marginBottom: '1rem'
|
||||
},
|
||||
footerText: {
|
||||
lineHeight: '1.6',
|
||||
opacity: 0.9
|
||||
},
|
||||
copyright: {
|
||||
textAlign: 'center',
|
||||
marginTop: '2rem',
|
||||
paddingTop: '2rem',
|
||||
borderTop: '1px solid rgba(255, 255, 255, 0.2)',
|
||||
opacity: 0.7
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={styles.container}>
|
||||
{/* Header */}
|
||||
<header style={styles.header}>
|
||||
<div style={styles.logo}>
|
||||
<span>🏪</span>
|
||||
<span>TECHNORAMA</span>
|
||||
</div>
|
||||
<nav>
|
||||
<ul style={styles.nav}>
|
||||
<li><a href="#" style={styles.navLink}>Home</a></li>
|
||||
<li><a href="#" style={styles.navLink}>Services</a></li>
|
||||
<li><a href="#" style={styles.navLink}>Product</a></li>
|
||||
<li><a href="#" style={styles.navLink}>Academy</a></li>
|
||||
<li><a href="#" style={styles.navLink}>About</a></li>
|
||||
<li><a href="#" style={styles.navLink}>Contact</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<button style={styles.ctaButton}>Sign Up Now</button>
|
||||
</header>
|
||||
|
||||
{/* Hero Section */}
|
||||
<section style={styles.hero}>
|
||||
<div style={styles.heroContent}>
|
||||
<h1 style={styles.heroTitle}>Kedai Master</h1>
|
||||
<p style={styles.heroText}>
|
||||
Platform Point of Sale terdepan yang dirancang khusus untuk meningkatkan
|
||||
kepuasan operational kafe dan restoran milik KM. Dengan sistem yang fleksibel,
|
||||
terpercaya, dan efisien.
|
||||
</p>
|
||||
</div>
|
||||
<div style={styles.heroImage}>
|
||||
<div style={styles.coffeeIcon}>☕</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Features Section */}
|
||||
<section style={styles.features}>
|
||||
<h2 style={styles.featuresTitle}>Fitur Unggulan</h2>
|
||||
<div style={styles.featuresGrid}>
|
||||
<div style={styles.featureCard}>
|
||||
<h3 style={styles.featureTitle}>Manajemen Tenant & Kasir</h3>
|
||||
<p style={styles.featureText}>
|
||||
Sistem untuk mengatur dan mengoptimalkan kinerja seluruh tenant.
|
||||
</p>
|
||||
</div>
|
||||
<div style={styles.featureCard}>
|
||||
<h3 style={styles.featureTitle}>QR Pemesanan di Meja</h3>
|
||||
<p style={styles.featureText}>
|
||||
Tamu restoran langsung dan mengoptimalkan waktu pemesanan dan pelayanan.
|
||||
</p>
|
||||
</div>
|
||||
<div style={styles.featureCard}>
|
||||
<h3 style={styles.featureTitle}>Otomatisasi Pesanan & Keuangan</h3>
|
||||
<p style={styles.featureText}>
|
||||
Mengatur operasional anda dengan otomatisasi pesanan dan keuangan.
|
||||
</p>
|
||||
</div>
|
||||
<div style={styles.featureCard}>
|
||||
<h3 style={styles.featureTitle}>Request & Voting Lagu</h3>
|
||||
<p style={styles.featureText}>
|
||||
Tamu dapat meminta lagu untuk diputar di restoran dan memberikan suasana.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* App Showcase */}
|
||||
<div style={styles.appShowcase}>
|
||||
<div style={styles.appContent}>
|
||||
<h3 style={styles.appTitle}>
|
||||
Gak perlu repot anti jam kerja yang baik bozen lagi.
|
||||
Tinggal scan QR yang ada di meja, langsung bisa udah langsung workflow
|
||||
</h3>
|
||||
<p style={styles.appText}>
|
||||
© 2025 KEDAIMASTERPBM.COM
|
||||
</p>
|
||||
</div>
|
||||
<div style={styles.appImages}>
|
||||
<div style={{...styles.phoneScreen, ...styles.phoneScreen1}}>
|
||||
<div style={styles.screenHeader}></div>
|
||||
<div style={styles.screenContent}></div>
|
||||
</div>
|
||||
<div style={{...styles.phoneScreen, ...styles.phoneScreen2}}>
|
||||
<div style={styles.screenHeader}></div>
|
||||
<div style={styles.screenContent}></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{...styles.appShowcase, flexDirection: 'row-reverse', marginTop: '4rem'}}>
|
||||
<div style={styles.appContent}>
|
||||
<h3 style={styles.appTitle}>Desain Menu Modern</h3>
|
||||
<p style={styles.appText}>
|
||||
Tampilan menu yang familiar, menarik dan mudah dipahami sehingga customer bisa dengan mudah memahami visual yang menarik untuk pengalaman ordering yang maksimal untuk kafe dan restoran masa kini.
|
||||
</p>
|
||||
</div>
|
||||
<div style={styles.appImages}>
|
||||
<div style={{...styles.phoneScreen, ...styles.phoneScreen1}}>
|
||||
<div style={styles.screenHeader}></div>
|
||||
<div style={styles.screenContent}></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA Section */}
|
||||
<section style={styles.cta}>
|
||||
<h2 style={styles.ctaTitle}>Siap Tingkatkan Bisnis Anda?</h2>
|
||||
<button style={styles.ctaButtonLarge}>Coba Kedai Master Sekarang</button>
|
||||
</section>
|
||||
|
||||
{/* Pricing Section */}
|
||||
<section style={styles.pricing}>
|
||||
<h2 style={styles.pricingTitle}>OUR PACK KEDAI MASTER</h2>
|
||||
<div style={styles.pricingCards}>
|
||||
<div style={styles.pricingCard}>
|
||||
<div style={styles.pricingBadge}>PAKET BASIC</div>
|
||||
<h3 style={styles.pricingPlan}>Starter Pack</h3>
|
||||
<div style={styles.pricingPrice}>Rp 245.000</div>
|
||||
<ul style={styles.pricingFeatures}>
|
||||
<li style={styles.pricingFeature}>1 user untuk admin</li>
|
||||
<li style={styles.pricingFeature}>Support via email</li>
|
||||
</ul>
|
||||
<button style={styles.pricingButton}>Pilih Paket</button>
|
||||
</div>
|
||||
|
||||
<div style={styles.pricingCard}>
|
||||
<div style={styles.pricingBadge}>PAKET SILVER</div>
|
||||
<h3 style={styles.pricingPlan}>Business Pack</h3>
|
||||
<div style={styles.pricingPrice}>Rp 499.000</div>
|
||||
<ul style={styles.pricingFeatures}>
|
||||
<li style={styles.pricingFeature}>Integrasi Meja & Jemput</li>
|
||||
<li style={styles.pricingFeature}>All permission & control</li>
|
||||
<li style={styles.pricingFeature}>Unlimited locations for pemasangan</li>
|
||||
</ul>
|
||||
<button style={styles.pricingButton}>Pilih Paket</button>
|
||||
</div>
|
||||
|
||||
<div style={styles.pricingCard}>
|
||||
<div style={styles.pricingBadge}>PAKET GOLD</div>
|
||||
<h3 style={styles.pricingPlan}>Enterprise Pack</h3>
|
||||
<div style={styles.pricingPrice}>Rp 849.000</div>
|
||||
<ul style={styles.pricingFeatures}>
|
||||
<li style={styles.pricingFeature}>All benefits unlimited fitures &</li>
|
||||
<li style={styles.pricingFeature}>Multi outlet & multi users</li>
|
||||
<li style={styles.pricingFeature}>Integrasi fitur locations</li>
|
||||
</ul>
|
||||
<button style={styles.pricingButton}>Pilih Paket</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<footer style={styles.footer}>
|
||||
<div style={styles.footerWave}></div>
|
||||
<div style={styles.footerContent}>
|
||||
<div style={styles.footerSection}>
|
||||
<h3 style={styles.footerTitle}>Contact Us</h3>
|
||||
<p style={styles.footerText}>
|
||||
Jalan ABC No. 123, Kota Surabaya, Jawa Timur 60123<br/>
|
||||
Phone: +62 123 456 7890<br/>
|
||||
Email: info@kedaimaster.com<br/>
|
||||
Website: www.kedaimaster.com
|
||||
</p>
|
||||
</div>
|
||||
<div style={styles.footerSection}>
|
||||
<h3 style={styles.footerTitle}>About Our Company</h3>
|
||||
<div style={styles.logo}>
|
||||
<span>🏪</span>
|
||||
<span>TECHNORAMA</span>
|
||||
</div>
|
||||
<p style={styles.footerText}>
|
||||
Kami adalah perusahaan yang berfokus pada solusi teknologi untuk industri F&B.
|
||||
Dengan pengalaman bertahun-tahun, kami berkomitmen memberikan layanan terbaik.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style={styles.copyright}>
|
||||
<p>© 2025 Kedai Master by Technorama. All rights reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default KedaiMasterLanding;
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Container, Row, Col, Card, Button, Tabs, Tab, Form } from "react-bootstrap";
|
||||
import { Container, Row, Col, Card, Button, Tabs, Tab, Form, ListGroup, Badge, Accordion } from "react-bootstrap";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import processProducts from '../../helper/processProducts';
|
||||
|
||||
const Dashboard = ({
|
||||
subscriptions,
|
||||
@@ -28,7 +27,31 @@ const Dashboard = ({
|
||||
}
|
||||
});
|
||||
|
||||
// 🔹 Handle input settings
|
||||
const [purchaseHistory, setPurchaseHistory] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const match = document.cookie.match(new RegExp('(^| )token=([^;]+)'));
|
||||
const token = match ? match[2] : null;
|
||||
|
||||
if (!token) {
|
||||
console.error("Token not found in cookies");
|
||||
return;
|
||||
}
|
||||
|
||||
fetch("https://bot.kediritechnopark.com/webhook/store-production/history", {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error("Failed to fetch purchase history");
|
||||
return res.json();
|
||||
})
|
||||
.then((data) => setPurchaseHistory(data))
|
||||
.catch((err) => console.error("Error fetching purchase history:", err));
|
||||
}, []);
|
||||
|
||||
|
||||
const handleSettingsChange = (field, value, nested = false) => {
|
||||
if (nested) {
|
||||
setSettings((prev) => ({
|
||||
@@ -40,7 +63,6 @@ const Dashboard = ({
|
||||
}
|
||||
};
|
||||
|
||||
// 🔹 Save profile
|
||||
const saveSettings = () => {
|
||||
fetch("https://bot.kediritechnopark.com/webhook-test/user-production/data", {
|
||||
method: "PUT",
|
||||
@@ -52,7 +74,6 @@ const Dashboard = ({
|
||||
.catch((err) => alert("Error updating settings: " + err));
|
||||
};
|
||||
|
||||
// 🔹 Group subscriptions
|
||||
const groupSubscriptionsByProductName = (subs) => {
|
||||
const result = {};
|
||||
subs.forEach((sub) => {
|
||||
@@ -82,7 +103,6 @@ const Dashboard = ({
|
||||
return result;
|
||||
};
|
||||
|
||||
// 🔹 Fetch produk berdasarkan subscription
|
||||
useEffect(() => {
|
||||
if (!subscriptions) return;
|
||||
|
||||
@@ -96,40 +116,39 @@ const Dashboard = ({
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
console.log(data)
|
||||
|
||||
const dataMap = {};
|
||||
data.forEach((item) => {
|
||||
dataMap[item.id] = { ...item, children: [] };
|
||||
});
|
||||
|
||||
// Masukkan anak-anak ke parent-nya
|
||||
data.forEach((item) => {
|
||||
if (item.sub_product_of && dataMap[item.sub_product_of]) {
|
||||
dataMap[item.sub_product_of].children.push(dataMap[item.id]);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(dataMap)
|
||||
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 description = "";
|
||||
console.log(productData)
|
||||
let realProductName = productData?.name || "";
|
||||
let site_url = productData?.site_url || "";
|
||||
if (!image && productData?.sub_product_of) {
|
||||
|
||||
if (productData?.sub_product_of) {
|
||||
const parent = data.find((p) => p.id === productData.sub_product_of);
|
||||
image = parent?.image || "";
|
||||
realProductName = parent.name;
|
||||
description = parent?.description || "";
|
||||
site_url = parent?.site_url || "";
|
||||
}
|
||||
|
||||
return {
|
||||
executeCheckout: group.product_name,
|
||||
id: group.product_id,
|
||||
name: group.product_name,
|
||||
realProductName,
|
||||
type: productData?.type || "product",
|
||||
image,
|
||||
description,
|
||||
site_url,
|
||||
price: productData?.price || 0,
|
||||
@@ -143,26 +162,21 @@ const Dashboard = ({
|
||||
children: dataMap[productData?.sub_product_of]?.children || []
|
||||
};
|
||||
});
|
||||
console.log(enrichedData)
|
||||
|
||||
setProducts(enrichedData);
|
||||
})
|
||||
.catch((err) => console.error("Fetch error:", err));
|
||||
}, [subscriptions]);
|
||||
|
||||
return (
|
||||
<Container fluid className="mt-4">
|
||||
<Tabs
|
||||
activeKey={activeTab}
|
||||
onSelect={(k) => setActiveTab(k)}
|
||||
className="mb-3"
|
||||
>
|
||||
{/* Produk Saya */}
|
||||
<Container fluid className="py-4 px-3 px-md-5">
|
||||
<Tabs activeKey={activeTab} onSelect={(k) => setActiveTab(k)} className="mb-3">
|
||||
<Tab eventKey="products" title="Produk Saya">
|
||||
<Row>
|
||||
{products.map((product) => (
|
||||
<Col md={4} key={product.id} className="mb-4">
|
||||
{products.map((product, i) => (
|
||||
<Col md={4} key={i} className="mb-4">
|
||||
<Card
|
||||
className={`h-100 shadow-sm ${hoveredCard === product.name ? "border-primary" : ""}`}
|
||||
className={`h-100 shadow-sm p-2 ${hoveredCard === product.name ? "border-primary" : ""}`}
|
||||
onMouseEnter={() => setHoveredCard(product.name)}
|
||||
onMouseLeave={() => setHoveredCard(null)}
|
||||
onClick={() => {
|
||||
@@ -170,26 +184,23 @@ const Dashboard = ({
|
||||
setShowedModal("product");
|
||||
}}
|
||||
>
|
||||
{product.image && (
|
||||
<Card.Img variant="top" src={product.image} />
|
||||
)}
|
||||
<Card.Body>
|
||||
<Card.Title>{product.name.split("%%%")[0]}</Card.Title>
|
||||
<Card.Text>{product.description}</Card.Text>
|
||||
<Card.Title>
|
||||
📦 {product.name.split("%%%")[0] + ' | ' + product.realProductName}
|
||||
</Card.Title>
|
||||
<Card.Text style={{ fontSize: "0.9rem", color: "#555" }}>
|
||||
{product.description}
|
||||
</Card.Text>
|
||||
</Card.Body>
|
||||
<Card.Footer>
|
||||
<Card.Footer className="d-flex justify-content-between align-items-center">
|
||||
<small className="text-muted">
|
||||
{product.unit_type === "duration"
|
||||
? `Valid until: ${product.end_date
|
||||
? new Date(product.end_date).toLocaleDateString()
|
||||
: "N/A"
|
||||
}`
|
||||
: `SISA TOKEN ${product.quantity || 0}`}
|
||||
? `Valid until: ${product.end_date ? new Date(product.end_date).toLocaleDateString() : "N/A"}`
|
||||
: `SISA TOKEN: ${product.quantity || 0}`}
|
||||
</small>
|
||||
<Button
|
||||
variant="primary"
|
||||
variant="outline-primary"
|
||||
size="sm"
|
||||
className="float-end"
|
||||
onClick={() => {
|
||||
setSelectedProduct(product);
|
||||
setShowedModal("product");
|
||||
@@ -203,22 +214,23 @@ const Dashboard = ({
|
||||
</Col>
|
||||
))}
|
||||
|
||||
{/* Tambah produk baru */}
|
||||
<Col md={4} className="mb-4">
|
||||
<Card
|
||||
className={`h-100 shadow-sm text-center align-items-center justify-content-center`}
|
||||
className="h-100 shadow-sm d-flex justify-content-center align-items-center text-center"
|
||||
onClick={() => navigate("/?tab=products")}
|
||||
>
|
||||
<Card.Body>
|
||||
<h5>+ Tambah produk baru</h5>
|
||||
<h5 style={{ color: "#007bff" }}>➕ Tambah Produk Baru</h5>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</Tab>
|
||||
|
||||
{/* Profil Pengguna */}
|
||||
<Tab eventKey="settings" title="Profil Pengguna">
|
||||
<Tab eventKey="settings" title="Profil">
|
||||
<Card className="shadow-sm border-0">
|
||||
<Card.Body>
|
||||
<Card.Title className="mb-4">Pengaturan Profil</Card.Title>
|
||||
<Form>
|
||||
<Row>
|
||||
<Col md={6}>
|
||||
@@ -232,7 +244,6 @@ const Dashboard = ({
|
||||
/>
|
||||
</Form.Group>
|
||||
</Col>
|
||||
|
||||
<Col md={6}>
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Email</Form.Label>
|
||||
@@ -249,7 +260,7 @@ const Dashboard = ({
|
||||
<Row>
|
||||
<Col md={6}>
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Full Name</Form.Label>
|
||||
<Form.Label>Nama Lengkap</Form.Label>
|
||||
<Form.Control
|
||||
value={settings.profile_data.name}
|
||||
onChange={(e) =>
|
||||
@@ -260,7 +271,7 @@ const Dashboard = ({
|
||||
</Col>
|
||||
<Col md={6}>
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Phone</Form.Label>
|
||||
<Form.Label>No. HP</Form.Label>
|
||||
<Form.Control
|
||||
value={settings.profile_data.phone}
|
||||
onChange={(e) =>
|
||||
@@ -272,8 +283,10 @@ const Dashboard = ({
|
||||
</Row>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Address</Form.Label>
|
||||
<Form.Label>Alamat</Form.Label>
|
||||
<Form.Control
|
||||
as="textarea"
|
||||
rows={2}
|
||||
value={settings.profile_data.address}
|
||||
onChange={(e) =>
|
||||
handleSettingsChange("address", e.target.value, true)
|
||||
@@ -284,7 +297,7 @@ const Dashboard = ({
|
||||
<Row>
|
||||
<Col md={6}>
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Company</Form.Label>
|
||||
<Form.Label>Perusahaan</Form.Label>
|
||||
<Form.Control
|
||||
value={settings.profile_data.company}
|
||||
onChange={(e) =>
|
||||
@@ -295,7 +308,7 @@ const Dashboard = ({
|
||||
</Col>
|
||||
<Col md={6}>
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Profile Image URL</Form.Label>
|
||||
<Form.Label>URL Gambar Profil</Form.Label>
|
||||
<Form.Control
|
||||
value={settings.profile_data.image}
|
||||
onChange={(e) =>
|
||||
@@ -309,7 +322,7 @@ const Dashboard = ({
|
||||
<Row>
|
||||
<Col md={6}>
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>New Password</Form.Label>
|
||||
<Form.Label>Password Baru</Form.Label>
|
||||
<Form.Control
|
||||
type="password"
|
||||
value={settings.password}
|
||||
@@ -321,23 +334,105 @@ const Dashboard = ({
|
||||
</Col>
|
||||
<Col md={6}>
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Re-type New Password</Form.Label>
|
||||
<Form.Label>Ketik Ulang Password</Form.Label>
|
||||
<Form.Control type="password" />
|
||||
</Form.Group>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Button variant="success" onClick={saveSettings}>
|
||||
Save Changes
|
||||
Simpan Perubahan
|
||||
</Button>
|
||||
</Form>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Tab>
|
||||
<Tab eventKey="orders" title="Pembelian">
|
||||
<Card className="shadow-sm border-0">
|
||||
<Card.Body>
|
||||
<Card.Title className="mb-4">Riwayat Pembelian</Card.Title>
|
||||
|
||||
{purchaseHistory.length === 0 ? (
|
||||
<p className="text-muted">Tidak ada riwayat pembelian.</p>
|
||||
) : (
|
||||
<Accordion defaultActiveKey={null} alwaysOpen>
|
||||
{purchaseHistory
|
||||
.sort((a, b) => new Date(b.created_at) - new Date(a.created_at))
|
||||
.map((order, idx) => {
|
||||
const createdAt = new Date(order.created_at);
|
||||
|
||||
// Konversi status ke label yang lebih ramah pengguna
|
||||
const statusLabel = {
|
||||
failed: "Transaksi Dibatalkan",
|
||||
pending: "Menunggu Pembayaran",
|
||||
completed: "Sukses",
|
||||
}[order.status] || order.status;
|
||||
|
||||
// Tentukan warna/status visual
|
||||
const statusVariant = {
|
||||
failed: "danger",
|
||||
pending: "warning",
|
||||
completed: "success",
|
||||
}[order.status] || "secondary";
|
||||
|
||||
return (
|
||||
<Accordion.Item eventKey={String(idx)} key={order.transaction_id + "-" + order.detailed_transactions_id}>
|
||||
<Accordion.Header>
|
||||
<div className="d-flex flex-column flex-md-row justify-content-between w-100">
|
||||
<div>
|
||||
<strong>{order.product_name.replace(/\t/g, " ")}</strong>
|
||||
<div className="text-muted small">
|
||||
ID: {order.transaction_id} • {createdAt.toLocaleString("id-ID")}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-md-end mt-2 mt-md-0">
|
||||
<Badge bg={statusVariant} className="text-capitalize">
|
||||
{statusLabel}
|
||||
</Badge>
|
||||
<div>
|
||||
<strong className="ms-2">Rp {order.amount.toLocaleString("id-ID")}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Accordion.Header>
|
||||
<Accordion.Body>
|
||||
<Row>
|
||||
<Col md={6}>
|
||||
<p className="mb-1"><strong>Metode Pembayaran:</strong> {order.payment_method || "N/A"}</p>
|
||||
<p className="mb-1"><strong>Status:</strong> {statusLabel}</p>
|
||||
<p className="mb-1"><strong>Tanggal Transaksi:</strong> {createdAt.toLocaleString("id-ID")}</p>
|
||||
</Col>
|
||||
<Col md={6} className="text-md-end">
|
||||
{order.status == 'failed' && (
|
||||
<div>
|
||||
<p className="text-danger small">
|
||||
Jika Anda sudah melakukan pembayaran, silakan konfirmasi manual.
|
||||
</p>
|
||||
<Button
|
||||
variant="outline-danger"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
alert("Fungsi konfirmasi manual belum diimplementasikan.");
|
||||
}}
|
||||
>
|
||||
Konfirmasi Manual
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Accordion.Body>
|
||||
</Accordion.Item>
|
||||
);
|
||||
})}
|
||||
</Accordion>
|
||||
)}
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Tab>
|
||||
|
||||
{/* Orders */}
|
||||
<Tab eventKey="orders" title="Pembelian">
|
||||
<h4>My Orders</h4>
|
||||
<p>Orders list will be displayed here.</p>
|
||||
</Tab>
|
||||
|
||||
|
||||
</Tabs>
|
||||
</Container>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user