asset path and iframe setting

This commit is contained in:
sandhiya-hepl
2026-07-16 10:57:25 +05:30
parent ed459d5943
commit 5b27debfa8
13 changed files with 131 additions and 67 deletions
+8 -7
View File
@@ -1,4 +1,4 @@
# Deployed under /citpl_website/ Apache must proxy API to the Node server. # Deployed under /citpl_website/ - Apache must proxy API to the Node server.
# Requires: mod_rewrite, mod_proxy, mod_proxy_http, mod_headers # Requires: mod_rewrite, mod_proxy, mod_proxy_http, mod_headers
# Start Node on the host: npm run server (or pm2 start server/index.js) # Start Node on the host: npm run server (or pm2 start server/index.js)
@@ -11,17 +11,18 @@
RewriteRule ^uploads/(.*)$ http://127.0.0.1:3001/uploads/$1 [P,L] RewriteRule ^uploads/(.*)$ http://127.0.0.1:3001/uploads/$1 [P,L]
</IfModule> </IfModule>
# Allow admin login preview iframe (same origin) # Allow embedding from this site and https://demo.cavinkare.in/
# X-Frame-Options cannot allow a third origin - unset it and use CSP only.
<IfModule mod_headers.c> <IfModule mod_headers.c>
Header always edit Content-Security-Policy "frame-ancestors 'none'" "frame-ancestors 'self'" Header always unset X-Frame-Options
Header always edit X-Frame-Options "DENY" "SAMEORIGIN" Header unset X-Frame-Options
Header always edit Content-Security-Policy "frame-ancestors 'none'" "frame-ancestors 'self' https://demo.cavinkare.in/"
<FilesMatch "^(preview\.html|index\.html)$"> <FilesMatch "^(preview\.html|index\.html|admin\.html)$">
Header unset X-Frame-Options Header unset X-Frame-Options
Header always unset X-Frame-Options Header always unset X-Frame-Options
Header unset Content-Security-Policy Header unset Content-Security-Policy
Header always unset Content-Security-Policy Header always unset Content-Security-Policy
Header always set X-Frame-Options "SAMEORIGIN" Header always set Content-Security-Policy "frame-ancestors 'self' https://demo.cavinkare.in/"
Header always set Content-Security-Policy "frame-ancestors 'self'"
</FilesMatch> </FilesMatch>
</IfModule> </IfModule>
-1
View File
File diff suppressed because one or more lines are too long
+1
View File
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{c as e,l as t,m as n,u as r}from"./x-DnG5sti3.js";import{r as i,t as a}from"./App-CuujNhcz.js";var o=n(r(),1),s=t(),c=e();(0,s.createRoot)(document.getElementById(`root`)).render((0,c.jsx)(o.StrictMode,{children:(0,c.jsx)(i,{children:(0,c.jsx)(a,{})})})); import{c as e,l as t,m as n,u as r}from"./x-DnG5sti3.js";import{r as i,t as a}from"./App-g0Gkzm_v.js";var o=n(r(),1),s=t(),c=e();(0,s.createRoot)(document.getElementById(`root`)).render((0,c.jsx)(o.StrictMode,{children:(0,c.jsx)(i,{children:(0,c.jsx)(a,{})})}));
@@ -1 +1 @@
import{c as e,l as t,m as n,u as r}from"./x-DnG5sti3.js";import{n as i,r as a,t as o}from"./App-CuujNhcz.js";var s=n(r(),1),c=t(),l=e();function u(){return(0,s.useEffect)(()=>(document.body.classList.add(`admin-preview-embed`),()=>document.body.classList.remove(`admin-preview-embed`)),[]),(0,l.jsx)(o,{previewMode:!0})}(0,c.createRoot)(document.getElementById(`root`)).render((0,l.jsx)(s.StrictMode,{children:(0,l.jsx)(i.Provider,{value:!0,children:(0,l.jsx)(a,{previewMode:!0,children:(0,l.jsx)(u,{})})})})); import{c as e,l as t,m as n,u as r}from"./x-DnG5sti3.js";import{n as i,r as a,t as o}from"./App-g0Gkzm_v.js";var s=n(r(),1),c=t(),l=e();function u(){return(0,s.useEffect)(()=>(document.body.classList.add(`admin-preview-embed`),()=>document.body.classList.remove(`admin-preview-embed`)),[]),(0,l.jsx)(o,{previewMode:!0})}(0,c.createRoot)(document.getElementById(`root`)).render((0,l.jsx)(s.StrictMode,{children:(0,l.jsx)(i.Provider,{value:!0,children:(0,l.jsx)(a,{previewMode:!0,children:(0,l.jsx)(u,{})})})}));
+2 -2
View File
@@ -5,9 +5,9 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cavin Infotech | One Trusted Technology Partner</title> <title>Cavin Infotech | One Trusted Technology Partner</title>
<script type="module" crossorigin src="/citpl_website/assets/main-oTwpSzcd.js"></script> <script type="module" crossorigin src="/citpl_website/assets/main-BxILBfZ3.js"></script>
<link rel="modulepreload" crossorigin href="/citpl_website/assets/x-DnG5sti3.js"> <link rel="modulepreload" crossorigin href="/citpl_website/assets/x-DnG5sti3.js">
<link rel="modulepreload" crossorigin href="/citpl_website/assets/App-CuujNhcz.js"> <link rel="modulepreload" crossorigin href="/citpl_website/assets/App-g0Gkzm_v.js">
<link rel="stylesheet" crossorigin href="/citpl_website/assets/App-DpUt8oMg.css"> <link rel="stylesheet" crossorigin href="/citpl_website/assets/App-DpUt8oMg.css">
</head> </head>
<body> <body>
+2 -2
View File
@@ -4,9 +4,9 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CITPL Preview</title> <title>CITPL Preview</title>
<script type="module" crossorigin src="/citpl_website/assets/preview-C0prGlYe.js"></script> <script type="module" crossorigin src="/citpl_website/assets/preview-wEp9RUMD.js"></script>
<link rel="modulepreload" crossorigin href="/citpl_website/assets/x-DnG5sti3.js"> <link rel="modulepreload" crossorigin href="/citpl_website/assets/x-DnG5sti3.js">
<link rel="modulepreload" crossorigin href="/citpl_website/assets/App-CuujNhcz.js"> <link rel="modulepreload" crossorigin href="/citpl_website/assets/App-g0Gkzm_v.js">
<link rel="stylesheet" crossorigin href="/citpl_website/assets/App-DpUt8oMg.css"> <link rel="stylesheet" crossorigin href="/citpl_website/assets/App-DpUt8oMg.css">
<link rel="stylesheet" crossorigin href="/citpl_website/assets/preview-DknkzKsF.css"> <link rel="stylesheet" crossorigin href="/citpl_website/assets/preview-DknkzKsF.css">
</head> </head>
+8 -7
View File
@@ -1,4 +1,4 @@
# Deployed under /citpl_website/ Apache must proxy API to the Node server. # Deployed under /citpl_website/ - Apache must proxy API to the Node server.
# Requires: mod_rewrite, mod_proxy, mod_proxy_http, mod_headers # Requires: mod_rewrite, mod_proxy, mod_proxy_http, mod_headers
# Start Node on the host: npm run server (or pm2 start server/index.js) # Start Node on the host: npm run server (or pm2 start server/index.js)
@@ -11,17 +11,18 @@
RewriteRule ^uploads/(.*)$ http://127.0.0.1:3001/uploads/$1 [P,L] RewriteRule ^uploads/(.*)$ http://127.0.0.1:3001/uploads/$1 [P,L]
</IfModule> </IfModule>
# Allow admin login preview iframe (same origin) # Allow embedding from this site and https://demo.cavinkare.in/
# X-Frame-Options cannot allow a third origin - unset it and use CSP only.
<IfModule mod_headers.c> <IfModule mod_headers.c>
Header always edit Content-Security-Policy "frame-ancestors 'none'" "frame-ancestors 'self'" Header always unset X-Frame-Options
Header always edit X-Frame-Options "DENY" "SAMEORIGIN" Header unset X-Frame-Options
Header always edit Content-Security-Policy "frame-ancestors 'none'" "frame-ancestors 'self' https://demo.cavinkare.in/"
<FilesMatch "^(preview\.html|index\.html)$"> <FilesMatch "^(preview\.html|index\.html|admin\.html)$">
Header unset X-Frame-Options Header unset X-Frame-Options
Header always unset X-Frame-Options Header always unset X-Frame-Options
Header unset Content-Security-Policy Header unset Content-Security-Policy
Header always unset Content-Security-Policy Header always unset Content-Security-Policy
Header always set X-Frame-Options "SAMEORIGIN" Header always set Content-Security-Policy "frame-ancestors 'self' https://demo.cavinkare.in/"
Header always set Content-Security-Policy "frame-ancestors 'self'"
</FilesMatch> </FilesMatch>
</IfModule> </IfModule>
+26 -26
View File
@@ -1,6 +1,6 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
const BASE = import.meta.env.BASE_URL; import { assetUrl } from './lib/assetUrl';
import { motion, useScroll, useTransform, useMotionValueEvent, useReducedMotion } from 'framer-motion'; import { motion, useScroll, useTransform, useMotionValueEvent, useReducedMotion } from 'framer-motion';
import { useContent } from './context/ContentContext'; import { useContent } from './context/ContentContext';
import { usePreviewMode } from './context/PreviewContext'; import { usePreviewMode } from './context/PreviewContext';
@@ -566,7 +566,7 @@ export default function App({ previewMode = false }) {
borderBottom: activeSection === 'products' ? '1px solid rgba(15, 23, 42, 0.08)' : '1px solid rgba(255, 255, 255, 0.05)' borderBottom: activeSection === 'products' ? '1px solid rgba(15, 23, 42, 0.08)' : '1px solid rgba(255, 255, 255, 0.05)'
}}> }}>
<div style={{ display: 'flex', alignItems: 'center' }}> <div style={{ display: 'flex', alignItems: 'center' }}>
<img src={`${BASE}assets/cavin_logo.svg`} alt="Cavin Infotech Logo" style={{ height: isMobile ? '38px' : '50px', display: 'block' }} /> <img src={assetUrl('/assets/cavin_logo.svg')} alt="Cavin Infotech Logo" style={{ height: isMobile ? '38px' : '50px', display: 'block' }} />
</div> </div>
{!isMobile && ( {!isMobile && (
@@ -608,7 +608,7 @@ export default function App({ previewMode = false }) {
); );
})} })}
<div className="btn-sparkle-wrapper"> <div className="btn-sparkle-wrapper">
<img src={`${BASE}assets/button_hover_effect.png`} className="btn-sparkles" alt="" /> <img src={assetUrl('/assets/button_hover_effect.png')} className="btn-sparkles" alt="" />
<button className="btn-talk" onClick={() => setIsStrategyOpen(true)}> <button className="btn-talk" onClick={() => setIsStrategyOpen(true)}>
Book a Strategy Call Book a Strategy Call
</button> </button>
@@ -728,7 +728,7 @@ export default function App({ previewMode = false }) {
pointerEvents: 'none' pointerEvents: 'none'
}} }}
> >
<source src={`${hero.video}?v=2`} type="video/mp4" /> <source src={`${assetUrl(hero.video)}?v=2`} type="video/mp4" />
Your browser does not support the video tag. Your browser does not support the video tag.
</video> </video>
@@ -937,7 +937,7 @@ export default function App({ previewMode = false }) {
{/* Globe Visual */} {/* Globe Visual */}
<div style={{ position: 'relative', width: '280px', height: '280px', display: 'flex', justifyContent: 'center', alignItems: 'center' }}> <div style={{ position: 'relative', width: '280px', height: '280px', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<img <img
src={`${BASE}assets/GlobeVector.svg`} src={assetUrl('/assets/GlobeVector.svg')}
alt="Globe Map" alt="Globe Map"
style={{ width: '100%', height: '100%', objectFit: 'contain', opacity: 0.3 }} style={{ width: '100%', height: '100%', objectFit: 'contain', opacity: 0.3 }}
/> />
@@ -962,7 +962,7 @@ export default function App({ previewMode = false }) {
padding: '0.2rem', padding: '0.2rem',
}}> }}>
<img <img
src={logo.image} src={assetUrl(logo.image)}
alt={logo.name} alt={logo.name}
style={{ maxHeight: '100%', maxWidth: '100%', objectFit: 'contain' }} style={{ maxHeight: '100%', maxWidth: '100%', objectFit: 'contain' }}
/> />
@@ -982,7 +982,7 @@ export default function App({ previewMode = false }) {
{/* Globe in background - Increased Size & Brightness */} {/* Globe in background - Increased Size & Brightness */}
<img <img
src={`${BASE}assets/GlobeVector.svg`} src={assetUrl('/assets/GlobeVector.svg')}
alt="Globe Map" alt="Globe Map"
style={{ style={{
position: 'absolute', position: 'absolute',
@@ -1045,18 +1045,18 @@ export default function App({ previewMode = false }) {
{/* orbital logos with smooth continuous motion in horizontally elongated oval paths - No Skewing */} {/* orbital logos with smooth continuous motion in horizontally elongated oval paths - No Skewing */}
{/* Outer Orbit - Increased size further to radiusX=520, radiusY=320 */} {/* Outer Orbit - Increased size further to radiusX=520, radiusY=320 */}
<OrbitalLogo src={`${BASE}assets/Frame 33.svg`} alt="MRF Logo" radiusX={520} radiusY={320} startAngle={0} duration={60} clockwise={true} size={170} /> <OrbitalLogo src={assetUrl('/assets/Frame 33.svg')} alt="MRF Logo" radiusX={520} radiusY={320} startAngle={0} duration={60} clockwise={true} size={170} />
<OrbitalLogo src={`${BASE}assets/Frame 31.svg`} alt="ACA Global Logo" radiusX={520} radiusY={320} startAngle={78} duration={60} clockwise={true} size={175} /> <OrbitalLogo src={assetUrl('/assets/Frame 31.svg')} alt="ACA Global Logo" radiusX={520} radiusY={320} startAngle={78} duration={60} clockwise={true} size={175} />
<OrbitalLogo src={`${BASE}assets/Frame 30.svg`} alt="Polyhose Logo" radiusX={520} radiusY={320} startAngle={144} duration={60} clockwise={true} size={170} /> <OrbitalLogo src={assetUrl('/assets/Frame 30.svg')} alt="Polyhose Logo" radiusX={520} radiusY={320} startAngle={144} duration={60} clockwise={true} size={170} />
<OrbitalLogo src={`${BASE}assets/Frame 32.svg`} alt="Dawn Pictures Logo" radiusX={520} radiusY={320} startAngle={216} duration={60} clockwise={true} size={180} /> <OrbitalLogo src={assetUrl('/assets/Frame 32.svg')} alt="Dawn Pictures Logo" radiusX={520} radiusY={320} startAngle={216} duration={60} clockwise={true} size={180} />
<OrbitalLogo src={`${BASE}assets/Frame 35.svg`} alt="Hero Logo" radiusX={520} radiusY={320} startAngle={282} duration={60} clockwise={true} size={170} /> <OrbitalLogo src={assetUrl('/assets/Frame 35.svg')} alt="Hero Logo" radiusX={520} radiusY={320} startAngle={282} duration={60} clockwise={true} size={170} />
{/* Inner Orbit - Adjusted size to radiusX=360, radiusY=225 */} {/* Inner Orbit - Adjusted size to radiusX=360, radiusY=225 */}
<OrbitalLogo src={`${BASE}assets/Frame 28.svg`} alt="Valeo Logo" radiusX={360} radiusY={225} startAngle={36} duration={40} clockwise={false} size={170} /> <OrbitalLogo src={assetUrl('/assets/Frame 28.svg')} alt="Valeo Logo" radiusX={360} radiusY={225} startAngle={36} duration={40} clockwise={false} size={170} />
<OrbitalLogo src={`${BASE}assets/Frame 36.svg`} alt="BorgWarner Logo" radiusX={360} radiusY={225} startAngle={108} duration={40} clockwise={false} size={170} /> <OrbitalLogo src={assetUrl('/assets/Frame 36.svg')} alt="BorgWarner Logo" radiusX={360} radiusY={225} startAngle={108} duration={40} clockwise={false} size={170} />
<OrbitalLogo src={`${BASE}assets/kone.png`} alt="KONE Logo" radiusX={360} radiusY={225} startAngle={180} duration={40} clockwise={false} size={160} /> <OrbitalLogo src={assetUrl('/assets/kone.png')} alt="KONE Logo" radiusX={360} radiusY={225} startAngle={180} duration={40} clockwise={false} size={160} />
<OrbitalLogo src={`${BASE}assets/Frame 34.svg`} alt="Chola MS Logo" radiusX={360} radiusY={225} startAngle={252} duration={40} clockwise={false} size={170} /> <OrbitalLogo src={assetUrl('/assets/Frame 34.svg')} alt="Chola MS Logo" radiusX={360} radiusY={225} startAngle={252} duration={40} clockwise={false} size={170} />
<OrbitalLogo src={`${BASE}assets/Frame 27.svg`} alt="CavinKare Logo" radiusX={360} radiusY={225} startAngle={324} duration={40} clockwise={false} size={180} /> <OrbitalLogo src={assetUrl('/assets/Frame 27.svg')} alt="CavinKare Logo" radiusX={360} radiusY={225} startAngle={324} duration={40} clockwise={false} size={180} />
</div> </div>
)} )}
@@ -1104,7 +1104,7 @@ export default function App({ previewMode = false }) {
transition: 'all 0.3s ease' transition: 'all 0.3s ease'
}}> }}>
<img <img
src={cert.image} src={assetUrl(cert.image)}
alt={cert.alt} alt={cert.alt}
style={{ style={{
height: idx === 0 ? '85%' : idx === 3 ? '55%' : idx === 4 ? '100%' : '80%', height: idx === 0 ? '85%' : idx === 3 ? '55%' : idx === 4 ? '100%' : '80%',
@@ -1369,7 +1369,7 @@ export default function App({ previewMode = false }) {
<div className="glass-card" style={{ <div className="glass-card" style={{
padding: '2rem 1.8rem', padding: '2rem 1.8rem',
borderRadius: '20px', borderRadius: '20px',
background: `linear-gradient(to bottom, rgba(2, 7, 16, 0.95) 0%, rgba(2, 7, 16, 0.6) 45%, rgba(2, 7, 16, 0) 85%), url(${BASE}assets/whyus_hologram.jpg)`, background: `linear-gradient(to bottom, rgba(2, 7, 16, 0.95) 0%, rgba(2, 7, 16, 0.6) 45%, rgba(2, 7, 16, 0) 85%), url(${assetUrl('/assets/whyus_hologram.jpg')})`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
border: '1px solid rgba(255, 255, 255, 0.06)', border: '1px solid rgba(255, 255, 255, 0.06)',
@@ -1431,7 +1431,7 @@ export default function App({ previewMode = false }) {
height: '100%' height: '100%'
}}> }}>
<img <img
src={`${BASE}assets/whyus_arab_meeting.jpg`} src={assetUrl('/assets/whyus_arab_meeting.jpg')}
alt="Corporate collaboration" alt="Corporate collaboration"
style={{ width: '100%', height: '100%', objectFit: 'cover' }} style={{ width: '100%', height: '100%', objectFit: 'cover' }}
/> />
@@ -1501,7 +1501,7 @@ export default function App({ previewMode = false }) {
height: '100%' height: '100%'
}}> }}>
<img <img
src={`${BASE}assets/whyus_vr_man.jpg`} src={assetUrl('/assets/whyus_vr_man.jpg')}
alt="Sunset boardroom" alt="Sunset boardroom"
style={{ width: '100%', height: '100%', objectFit: 'cover' }} style={{ width: '100%', height: '100%', objectFit: 'cover' }}
/> />
@@ -1515,7 +1515,7 @@ export default function App({ previewMode = false }) {
border: '1px solid rgba(255, 255, 255, 0.06)', border: '1px solid rgba(255, 255, 255, 0.06)',
position: 'relative', position: 'relative',
overflow: 'hidden', overflow: 'hidden',
backgroundImage: `linear-gradient(to bottom, rgba(2, 7, 16, 0.95) 0%, rgba(2, 7, 16, 0.6) 45%, rgba(2, 7, 16, 0) 85%), url(${BASE}assets/whyus_hologram.jpg)`, backgroundImage: `linear-gradient(to bottom, rgba(2, 7, 16, 0.95) 0%, rgba(2, 7, 16, 0.6) 45%, rgba(2, 7, 16, 0) 85%), url(${assetUrl('/assets/whyus_hologram.jpg')})`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
display: 'flex', display: 'flex',
@@ -1695,7 +1695,7 @@ export default function App({ previewMode = false }) {
> >
{/* Full-bleed image */} {/* Full-bleed image */}
<img <img
src={insight.image} src={assetUrl(insight.image)}
alt={insight.title} alt={insight.title}
className="blog-card-img" className="blog-card-img"
style={{ style={{
@@ -1775,7 +1775,7 @@ export default function App({ previewMode = false }) {
padding: '4rem 0 0 0', padding: '4rem 0 0 0',
}}> }}>
<img <img
src={`${BASE}assets/citp_footer_design.png`} src={assetUrl('/assets/citp_footer_design.png')}
alt="Cavin Infotech Logo Divider" alt="Cavin Infotech Logo Divider"
style={{ style={{
width: '100%', width: '100%',
@@ -1851,7 +1851,7 @@ export default function App({ previewMode = false }) {
{/* Phone mockup container on mobile */} {/* Phone mockup container on mobile */}
<div style={{ width: '100%', height: '180px', position: 'relative', overflow: 'hidden', borderRadius: '12px' }}> <div style={{ width: '100%', height: '180px', position: 'relative', overflow: 'hidden', borderRadius: '12px' }}>
<img <img
src={footer.cta.image} src={assetUrl(footer.cta.image)}
alt="Phone preview" alt="Phone preview"
style={{ style={{
position: 'absolute', position: 'absolute',
@@ -1870,7 +1870,7 @@ export default function App({ previewMode = false }) {
<Reveal> <Reveal>
<div style={{ position: 'relative', width: '100%', marginBottom: '3rem' }}> <div style={{ position: 'relative', width: '100%', marginBottom: '3rem' }}>
<img <img
src={footer.cta.image} src={assetUrl(footer.cta.image)}
alt="CTA Banner" alt="CTA Banner"
style={{ style={{
width: '100%', width: '100%',
+3 -2
View File
@@ -1,6 +1,7 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { motion, AnimatePresence, useReducedMotion } from 'framer-motion'; import { motion, AnimatePresence, useReducedMotion } from 'framer-motion';
import { ArrowRight, Clock } from 'lucide-react'; import { ArrowRight, Clock } from 'lucide-react';
import { assetUrl } from '../lib/assetUrl';
export default function FeaturedGallery({ gallery }) { export default function FeaturedGallery({ gallery }) {
const galleryData = gallery?.items || []; const galleryData = gallery?.items || [];
@@ -82,7 +83,7 @@ function ResponsiveGalleryWrapper({ galleryData, activeIdx, setActiveIdx }) {
border: '1px solid rgba(255, 255, 255, 0.06)', border: '1px solid rgba(255, 255, 255, 0.06)',
position: 'relative', position: 'relative',
height: isFirst ? '320px' : '240px', height: isFirst ? '320px' : '240px',
backgroundImage: `linear-gradient(to bottom, rgba(2, 7, 16, 0.3) 0%, rgba(2, 7, 16, 0.95) 100%), url(${item.image})`, backgroundImage: `linear-gradient(to bottom, rgba(2, 7, 16, 0.3) 0%, rgba(2, 7, 16, 0.95) 100%), url(${assetUrl(item.image)})`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
display: 'flex', display: 'flex',
@@ -171,7 +172,7 @@ function ResponsiveGalleryWrapper({ galleryData, activeIdx, setActiveIdx }) {
left: 0, left: 0,
right: 0, right: 0,
bottom: 0, bottom: 0,
backgroundImage: `url(${item.image})`, backgroundImage: `url(${assetUrl(item.image)})`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
zIndex: 1 zIndex: 1
+2 -1
View File
@@ -1,4 +1,5 @@
import { motion, useReducedMotion } from 'framer-motion'; import { motion, useReducedMotion } from 'framer-motion';
import { assetUrl } from '../lib/assetUrl';
const ACCENT_GRADIENTS = [ const ACCENT_GRADIENTS = [
'radial-gradient(circle at top right, rgba(139, 92, 246, 0.08) 0%, transparent 50%), linear-gradient(to bottom, #FAF9FA 0%, #F6F2F5 100%)', 'radial-gradient(circle at top right, rgba(139, 92, 246, 0.08) 0%, transparent 50%), linear-gradient(to bottom, #FAF9FA 0%, #F6F2F5 100%)',
@@ -114,7 +115,7 @@ export default function ProductCard({ product, isMobile, delay = 0, index = 0 })
background: 'transparent', background: 'transparent',
}}> }}>
<img <img
src={product.image} src={assetUrl(product.image)}
alt={`${product.name} dashboard`} alt={`${product.name} dashboard`}
style={{ style={{
position: 'absolute', position: 'absolute',
+15 -17
View File
@@ -1,23 +1,14 @@
import { createContext, useContext, useEffect, useState } from 'react'; import { createContext, useContext, useEffect, useState } from 'react';
import defaultContent from '../../server/seed/default-content.json'; import defaultContent from '../../server/seed/default-content.json';
import { API_BASE } from '../lib/apiBase'; import { API_BASE } from '../lib/apiBase';
import { prefixAssets } from '../lib/assetUrl';
const ContentContext = createContext(null); const ContentContext = createContext(null);
const BASE = import.meta.env.BASE_URL; function allowedPreviewOrigins() {
const origins = new Set(['https://demo.cavinkare.in/', 'https://demo.cavinkare.in']);
// Recursively prefix /assets/ paths with the Vite base URL if (typeof window !== 'undefined') origins.add(window.location.origin);
function prefixAssets(obj) { return origins;
if (typeof obj === 'string') {
return obj.startsWith('/assets/') ? `${BASE}${obj.slice(1)}` : obj;
}
if (Array.isArray(obj)) return obj.map(prefixAssets);
if (obj && typeof obj === 'object') {
const out = {};
for (const k in obj) out[k] = prefixAssets(obj[k]);
return out;
}
return obj;
} }
export function ContentProvider({ children, previewMode = false }) { export function ContentProvider({ children, previewMode = false }) {
@@ -51,9 +42,10 @@ export function ContentProvider({ children, previewMode = false }) {
if (!previewMode) return; if (!previewMode) return;
setLoading(false); setLoading(false);
const origins = allowedPreviewOrigins();
const onMessage = (event) => { const onMessage = (event) => {
if (event.origin !== window.location.origin) return; if (!origins.has(event.origin)) return;
if (event.data?.type !== 'ADMIN_PREVIEW' || !event.data.fullContent) return; if (event.data?.type !== 'ADMIN_PREVIEW' || !event.data.fullContent) return;
setContent(prefixAssets(event.data.fullContent)); setContent(prefixAssets(event.data.fullContent));
@@ -68,7 +60,13 @@ export function ContentProvider({ children, previewMode = false }) {
}; };
window.addEventListener('message', onMessage); window.addEventListener('message', onMessage);
window.parent?.postMessage({ type: 'PREVIEW_READY' }, window.location.origin); for (const origin of origins) {
try {
window.parent?.postMessage({ type: 'PREVIEW_READY' }, origin);
} catch {
/* ignore */
}
}
return () => window.removeEventListener('message', onMessage); return () => window.removeEventListener('message', onMessage);
}, [previewMode]); }, [previewMode]);
@@ -82,6 +80,6 @@ export function ContentProvider({ children, previewMode = false }) {
export function useContent() { export function useContent() {
const ctx = useContext(ContentContext); const ctx = useContext(ContentContext);
if (!ctx) throw new Error('useContent must be used within ContentProvider'); if (!ctx) throw new Error('useContent must be used within a ContentProvider');
return ctx; return ctx;
} }
+62
View File
@@ -0,0 +1,62 @@
/**
* Vite base, e.g. "/citpl_website/" in production and "/" in local dev.
*/
export const ASSET_BASE = (import.meta.env.BASE_URL || '/').endsWith('/')
? (import.meta.env.BASE_URL || '/')
: `${import.meta.env.BASE_URL || ''}/`;
/**
* Resolve content/seed asset paths for subdirectory deploys.
* "/assets/foo.svg" "/citpl_website/assets/foo.svg" in prod
*/
export function assetUrl(path) {
if (!path || typeof path !== 'string') return path;
if (/^(data:|blob:)/i.test(path)) return path;
// Absolute URL wrongly pointing at domain-root /assets or /uploads
if (/^https?:\/\//i.test(path)) {
try {
const u = new URL(path);
if (u.pathname.startsWith('/assets/') || u.pathname.startsWith('/uploads/')) {
const prefix = ASSET_BASE.replace(/\/$/, '');
if (!u.pathname.startsWith(`${prefix}/`)) {
u.pathname = `${prefix}${u.pathname}`;
return u.href;
}
}
return path;
} catch {
return path;
}
}
const prefix = ASSET_BASE.replace(/\/$/, '');
// Already under the site base
if (prefix && (path === prefix || path.startsWith(`${prefix}/`))) {
return path;
}
if (path.startsWith('/assets/') || path.startsWith('/uploads/')) {
return `${prefix}${path}`;
}
if (path.startsWith('assets/') || path.startsWith('uploads/')) {
return `${ASSET_BASE}${path}`;
}
return path;
}
/** Recursively rewrite asset strings inside content JSON. */
export function prefixAssets(obj) {
if (typeof obj === 'string') return assetUrl(obj);
if (Array.isArray(obj)) return obj.map(prefixAssets);
if (obj && typeof obj === 'object') {
const out = {};
for (const k in obj) out[k] = prefixAssets(obj[k]);
return out;
}
return obj;
}