feat(footer): implement dynamic social media URLs in footer component
This commit is contained in:
@@ -27,6 +27,22 @@ const head = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function buildSocialUrl(name, value) {
|
||||||
|
if (!value) return null;
|
||||||
|
if (/^https?:\/\//i.test(value)) return value;
|
||||||
|
const handle = value.replace(/^@/, "").replace(/^\+/, "");
|
||||||
|
switch (name) {
|
||||||
|
case "instagram":
|
||||||
|
return `https://instagram.com/${handle}`;
|
||||||
|
case "telegram":
|
||||||
|
return `https://t.me/${handle}`;
|
||||||
|
case "whatsapp":
|
||||||
|
return `https://wa.me/${handle.replace(/\D/g, "")}`;
|
||||||
|
default:
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Footer({ matchedCity }) {
|
function Footer({ matchedCity }) {
|
||||||
const socialMedias = [
|
const socialMedias = [
|
||||||
{
|
{
|
||||||
@@ -114,20 +130,31 @@ function Footer({ matchedCity }) {
|
|||||||
همراه با ما باشید:
|
همراه با ما باشید:
|
||||||
</p>
|
</p>
|
||||||
<ul className="flex items-center gap-[8px] sm:gap-[11px] md:gap-[13px] lg:gap-[15px]">
|
<ul className="flex items-center gap-[8px] sm:gap-[11px] md:gap-[13px] lg:gap-[15px]">
|
||||||
{socialMedias.map((item, idx) => (
|
{socialMedias.map((item, idx) => {
|
||||||
<li
|
const url = buildSocialUrl(item.name, matchedCity?.socialMedia?.[item.name]);
|
||||||
key={idx}
|
return (
|
||||||
className="p-2.5 bg-[#EAECFA] hover-rotate-icon cursor-pointer rounded-full w-fit"
|
<li
|
||||||
>
|
key={idx}
|
||||||
<Link
|
className="p-2.5 bg-[#EAECFA] hover-rotate-icon cursor-pointer rounded-full w-fit"
|
||||||
href={matchedCity?.socialMedia[item.name] || "/"}
|
|
||||||
className="min-h-fit"
|
|
||||||
aria-label={item.name}
|
|
||||||
>
|
>
|
||||||
{item.icon}
|
{url ? (
|
||||||
</Link>
|
<a
|
||||||
</li>
|
href={url}
|
||||||
))}
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="min-h-fit"
|
||||||
|
aria-label={item.name}
|
||||||
|
>
|
||||||
|
{item.icon}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<span className="min-h-fit" aria-label={item.name}>
|
||||||
|
{item.icon}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex lg:hidden justify-center w-full mt-2">
|
<div className="flex lg:hidden justify-center w-full mt-2">
|
||||||
|
|||||||
+19
-1
@@ -41,13 +41,31 @@ const nextConfig = {
|
|||||||
// Enable standalone output for Docker
|
// Enable standalone output for Docker
|
||||||
output: 'standalone',
|
output: 'standalone',
|
||||||
async headers() {
|
async headers() {
|
||||||
|
const isDev = process.env.NODE_ENV !== 'production';
|
||||||
|
let apiOrigin = '';
|
||||||
|
try {
|
||||||
|
apiOrigin = new URL(process.env.NEXT_PUBLIC_API_URL).origin;
|
||||||
|
} catch {
|
||||||
|
apiOrigin = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const connectSrc = [
|
||||||
|
"'self'",
|
||||||
|
apiOrigin,
|
||||||
|
'https://clinic-pro.ir',
|
||||||
|
'https://back-dev.clinic-pro.ir',
|
||||||
|
...(isDev ? ['ws:', 'wss:'] : []),
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
const csp = [
|
const csp = [
|
||||||
"default-src 'self'",
|
"default-src 'self'",
|
||||||
"img-src 'self' https: data: blob:",
|
"img-src 'self' https: data: blob:",
|
||||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval'",
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval'",
|
||||||
"style-src 'self' 'unsafe-inline'",
|
"style-src 'self' 'unsafe-inline'",
|
||||||
"font-src 'self' data:",
|
"font-src 'self' data:",
|
||||||
"connect-src 'self' https://api.clinic-pro.ir https://back-dev.clinic-pro.ir",
|
'connect-src ' + connectSrc,
|
||||||
"frame-ancestors 'none'",
|
"frame-ancestors 'none'",
|
||||||
"object-src 'none'",
|
"object-src 'none'",
|
||||||
"base-uri 'self'",
|
"base-uri 'self'",
|
||||||
|
|||||||
Reference in New Issue
Block a user