diff --git a/src/pages/PluginDetail.tsx b/src/pages/PluginDetail.tsx index 45440b6..cb631d0 100644 --- a/src/pages/PluginDetail.tsx +++ b/src/pages/PluginDetail.tsx @@ -7,6 +7,30 @@ import remarkGfm from 'remark-gfm'; import { useNavigate } from 'react-router-dom'; import SEO from '../components/SEO'; +const getYoutubeVideoId = (url: string) => { + if (!url) return null; + try { + if (url.includes('youtu.be/')) { + return url.split('youtu.be/')[1].split(/[?#]/)[0]; + } else if (url.includes('youtube.com/embed/')) { + return url.split('youtube.com/embed/')[1].split(/[?#]/)[0]; + } else if (url.includes('youtube.com/shorts/')) { + return url.split('youtube.com/shorts/')[1].split(/[?#]/)[0]; + } else if (url.includes('v=')) { + return url.split('v=')[1].split(/[&?#]/)[0]; + } + return null; + } catch { + return null; + } +}; + +const getYoutubeEmbedUrl = (url: string) => { + if (!url) return ''; + const videoId = getYoutubeVideoId(url); + return videoId ? `https://www.youtube.com/embed/${videoId}` : url; +}; + const REPORT_REASONS = [ 'Malware or malicious code', 'Stolen or plagiarised content', @@ -125,6 +149,44 @@ const PluginDetail = () => { const [selectedLanguage] = useState('en'); const [currentDescription, setCurrentDescription] = useState(''); + // Coupon state + const [couponCode, setCouponCode] = useState(''); + const [appliedCoupon, setAppliedCoupon] = useState(null); + const [couponDiscount, setCouponDiscount] = useState(0); + const [couponMessage, setCouponMessage] = useState<{ text: string; isError: boolean } | null>(null); + const [validatingCoupon, setValidatingCoupon] = useState(false); + + const handleApplyCoupon = async () => { + if (!couponCode.trim()) return; + setValidatingCoupon(true); + setCouponMessage(null); + try { + const res = await api.get(`/plugins/${id}/validate-coupon?code=${couponCode.trim()}`); + if (res.data.valid) { + setAppliedCoupon(couponCode.trim().toUpperCase()); + setCouponDiscount(res.data.discount_percent); + setCouponMessage({ text: res.data.message, isError: false }); + } else { + setAppliedCoupon(null); + setCouponDiscount(0); + setCouponMessage({ text: res.data.message || 'Invalid coupon code', isError: true }); + } + } catch { + setAppliedCoupon(null); + setCouponDiscount(0); + setCouponMessage({ text: 'Failed to validate coupon.', isError: true }); + } finally { + setValidatingCoupon(false); + } + }; + + const handleClearCoupon = () => { + setCouponCode(''); + setAppliedCoupon(null); + setCouponDiscount(0); + setCouponMessage(null); + }; + // Ownership const [ownsPlugin, setOwnsPlugin] = useState(false); const [ownershipChecked, setOwnershipChecked] = useState(false); @@ -166,7 +228,9 @@ const PluginDetail = () => { const fetchPlugin = () => { api.get(`/plugins/${id}`).then(res => { setPlugin(res.data); - if (res.data.screenshots?.length > 0) { + if (res.data.youtube_video_url) { + setMainScreenshot('video'); + } else if (res.data.screenshots?.length > 0) { setMainScreenshot(res.data.screenshots[0].path); } }); @@ -283,7 +347,8 @@ const PluginDetail = () => { // Does not own — go to Stripe checkout try { - const res = await api.post(`/plugins/${id}/checkout`); + const url = appliedCoupon ? `/plugins/${id}/checkout?coupon=${appliedCoupon}` : `/plugins/${id}/checkout`; + const res = await api.post(url); if (res.data.url) window.location.href = res.data.url; } catch (err: any) { if (err?.response?.status === 409) { @@ -504,30 +569,65 @@ const PluginDetail = () => {
- {/* SCREENSHOTS */} - {Array.isArray(plugin.screenshots) && plugin.screenshots.length > 0 && ( + {/* SCREENSHOTS / MEDIA GALLERY */} + {((Array.isArray(plugin.screenshots) && plugin.screenshots.length > 0) || plugin.youtube_video_url) && (
- Main { - const idx = plugin.screenshots.findIndex((s: any) => s.path === (mainScreenshot || plugin.screenshots[0].path)); - openLightbox(idx >= 0 ? idx : 0); - }} - /> -
- {plugin.screenshots.map((shot: any) => ( - Thumbnail setMainScreenshot(shot.path)} - /> - ))} -
+ {mainScreenshot === 'video' ? ( +
+