ok
This commit is contained in:
@@ -11,10 +11,60 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
|||||||
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 navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const [customName, setCustomName] = useState('');
|
||||||
|
const [status, setStatus] = useState('idle'); // 'idle' | 'checking' | 'available' | 'unavailable' | 'error'
|
||||||
|
|
||||||
|
|
||||||
|
const tokenCookie = document.cookie.split('; ').find(row => row.startsWith('token='));
|
||||||
|
const token = tokenCookie ? tokenCookie.split('=')[1] : '';
|
||||||
|
|
||||||
|
|
||||||
|
// Helper panggil API kamu (GET + token header)
|
||||||
|
async function checkProductAvailability(name, token) {
|
||||||
|
const url = `https://bot.kediritechnopark.com/webhook/store_production/check_p_availability?productId=${product.id}&name=${encodeURIComponent(name)}`;
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
Accept: 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error(`Server error ${res.status}`);
|
||||||
|
const data = await res.json(); // expected: { allowed: true|false }
|
||||||
|
return Boolean(data.allowed);
|
||||||
|
}
|
||||||
|
// Auto check saat user mengetik (debounce)
|
||||||
|
useEffect(() => {
|
||||||
|
if (product.unique_name == false) return;
|
||||||
|
|
||||||
|
const name = customName.trim();
|
||||||
|
if (!name) {
|
||||||
|
setStatus('idle');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let cancelled = false;
|
||||||
|
setStatus('checking');
|
||||||
|
|
||||||
|
const t = setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
const allowed = await checkProductAvailability(name, token);
|
||||||
|
if (cancelled) return;
|
||||||
|
setStatus(allowed ? 'available' : 'unavailable');
|
||||||
|
} catch (e) {
|
||||||
|
if (cancelled) return;
|
||||||
|
setStatus('error');
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
clearTimeout(t);
|
||||||
|
};
|
||||||
|
}, [customName, token]);
|
||||||
|
|
||||||
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] : '';
|
||||||
@@ -29,7 +79,7 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
|||||||
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)
|
||||||
);
|
);
|
||||||
console.log(hasMatchingSubscription)
|
|
||||||
// ✅ Check subscription first
|
// ✅ Check subscription first
|
||||||
if (hasMatchingSubscription) {
|
if (hasMatchingSubscription) {
|
||||||
const matching = subscriptions.filter(sub =>
|
const matching = subscriptions.filter(sub =>
|
||||||
@@ -126,26 +176,38 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!product.executeCheckout && willDo === 'checkout') {
|
if (willDo === 'checkout') {
|
||||||
onCheckout();
|
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/products&redirect_failed=https://kediritechnopark.com`;
|
|
||||||
}
|
|
||||||
if (setWillDo) setWillDo('');
|
if (setWillDo) setWillDo('');
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const priceColor = product.price === 0 ? '#059669' : '#2563eb';
|
const priceColor = product.price === 0 ? '#059669' : '#2563eb';
|
||||||
|
|
||||||
|
// Komponen kecil untuk menampilkan status teks
|
||||||
|
const StatusLine = () => {
|
||||||
|
if (status === 'idle') return null;
|
||||||
|
const map = {
|
||||||
|
checking: 'Memeriksa…',
|
||||||
|
available: 'Nama tersedia',
|
||||||
|
unavailable: 'Nama tidak tersedia',
|
||||||
|
error: 'Gagal memeriksa. Coba lagi.',
|
||||||
|
};
|
||||||
|
const color =
|
||||||
|
status === 'available'
|
||||||
|
? '#16a34a'
|
||||||
|
: status === 'unavailable'
|
||||||
|
? '#dc2626'
|
||||||
|
: status === 'checking'
|
||||||
|
? '#2563eb'
|
||||||
|
: '#6b7280';
|
||||||
|
return (
|
||||||
|
<div style={{ marginTop: 6, fontSize: 12, color }}>
|
||||||
|
{map[status]}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
{/* Default view */}
|
{/* Default view */}
|
||||||
@@ -239,8 +301,8 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
{/* Naming input */}
|
{/* 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>
|
||||||
@@ -249,18 +311,24 @@ const ProductDetail = ({ willDo, setWillDo, subscriptions, product, requestLogin
|
|||||||
placeholder="Nama produk..."
|
placeholder="Nama produk..."
|
||||||
className={styles.input}
|
className={styles.input}
|
||||||
value={customName}
|
value={customName}
|
||||||
onChange={(e) => setCustomName(e.target.value)}
|
onChange={(e) => {
|
||||||
style={{ width: '100%', padding: '8px', marginBottom: '16px', borderRadius: '10px' }}
|
const value = e.target.value.replace(/\s+/g, '-'); // Ganti spasi dengan -
|
||||||
|
setCustomName(value);
|
||||||
|
}}
|
||||||
|
style={{ width: '100%', padding: '8px', marginBottom: '8px', borderRadius: '10px' }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className={styles.buttonGroup}>
|
{product.unique_name && <StatusLine />}
|
||||||
|
|
||||||
|
<div className={styles.buttonGroup} style={{ marginTop: 12 }}>
|
||||||
<button className={styles.button} onClick={() => setShowNamingInput(false)}>
|
<button className={styles.button} onClick={() => setShowNamingInput(false)}>
|
||||||
Kembali
|
Kembali
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={styles.button}
|
className={styles.button}
|
||||||
onClick={onFinalCheckoutNewProduct}
|
onClick={onFinalCheckoutNewProduct}
|
||||||
disabled={customName.trim() === ''}
|
disabled={customName.trim() === '' || status !== 'available'}
|
||||||
|
title={status !== 'available' ? 'Nama belum tersedia' : 'Lanjut'}
|
||||||
>
|
>
|
||||||
Lanjut
|
Lanjut
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user