This commit is contained in:
dimitar 2025-04-26 17:46:59 +02:00
parent 1e38bd67a0
commit 967f9b414e
6 changed files with 252 additions and 217 deletions

View File

@ -3,8 +3,10 @@ export const corsConfig = {
"http://localhost:5173", "http://localhost:5173",
"http://localhost:4444", "http://localhost:4444",
"http://localhost:3000", "http://localhost:3000",
"https://www.placebo.mk", "https://www.imk.mk",
"https://placebo.mk", "https://imk.mk/",
// "https://www.placebo.mk",
// "https://placebo.mk",
"https://imkapi.oblak.solutions", "https://imkapi.oblak.solutions",
// "https://eu-assets.i.posthog.com", // "https://eu-assets.i.posthog.com",
// "https://app.posthog.com", // "https://app.posthog.com",

View File

@ -253,6 +253,26 @@ export class EmailService {
<p>If you didn't request this, please ignore this email or contact support if you have concerns.</p> <p>If you didn't request this, please ignore this email or contact support if you have concerns.</p>
<p>Best regards,<br>IMK Team</p> <p>Best regards,<br>IMK Team</p>
</div> </div>
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2>Барање за ресетирање лозинка</h2>
<p>Hello ${name},</p>
<p>Добивме барање за ресетирање лозинка. Кликнете на линкот подолу за да поставите нова лозинка:</p>
<p>
<a href="${resetLink}" style="
display: inline-block;
padding: 10px 20px;
background-color: #4F46E5;
color: white;
text-decoration: none;
border-radius: 5px;
">Ресетирај лозинка</a>
</p>
<p>This link will expire in 1 hour.</p>
<p>Линкот е валиден 1 час.</p>
<p>If you didn't request this, please ignore this email or contact support if you have concerns.</p>
<p>Доколку не сте побарале ресетирање на лозинка игнорирајте ја оваа е-пошта</p>
<p>Секое добро,<br>IMK Team</p>
</div>
`, `,
}; };
@ -287,6 +307,14 @@ export class EmailService {
<p>If you did not make this change, please contact our support team immediately.</p> <p>If you did not make this change, please contact our support team immediately.</p>
<p>Best regards,<br>IMK Team</p> <p>Best regards,<br>IMK Team</p>
</div> </div>
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2>Лозинката е успешно променена</h2>
<p>Здраво ${name},</p>
<p>Вашата лозинка е успешно променена.</p>
<p>Доколку не сте побарале промената на лозинката, ве молиме веднаш да не контактирате.</p>
<p>Секое добро,<br>IMK Team</p>
</div>
`, `,
}; };

View File

@ -32,43 +32,43 @@ async function bootstrap() {
}), }),
); );
app.use( // app.use(
helmet({ // helmet({
contentSecurityPolicy: { // contentSecurityPolicy: {
directives: { // directives: {
defaultSrc: ["'self'"], // defaultSrc: ["'self'"],
scriptSrc: [ // scriptSrc: [
"'self'", // "'self'",
"'unsafe-inline'", // "'unsafe-inline'",
"'unsafe-eval'", // "'unsafe-eval'",
"https://eu-assets.i.posthog.com", // "https://eu-assets.i.posthog.com",
"https://app.posthog.com", // "https://app.posthog.com",
"https://eu.posthog.com", // "https://eu.posthog.com",
], // ],
connectSrc: [ // connectSrc: [
"'self'", // "'self'",
"https://eu.posthog.com", // "https://eu.posthog.com",
"https://eu-assets.i.posthog.com", // "https://eu-assets.i.posthog.com",
"https://app.posthog.com", // "https://app.posthog.com",
], // ],
imgSrc: ["'self'", "data:", "https:"], // imgSrc: ["'self'", "data:", "https:"],
styleSrc: ["'self'", "'unsafe-inline'"], // styleSrc: ["'self'", "'unsafe-inline'"],
fontSrc: ["'self'", "data:", "https:"], // fontSrc: ["'self'", "data:", "https:"],
rameSrc: [ // rameSrc: [
"'self'", // "'self'",
"https://eu-assets.i.posthog.com", // "https://eu-assets.i.posthog.com",
"https://app.posthog.com", // "https://app.posthog.com",
], // ],
}, // },
}, // },
crossOriginResourcePolicy: { // crossOriginResourcePolicy: {
policy: "cross-origin", // policy: "cross-origin",
}, // },
crossOriginOpenerPolicy: { // crossOriginOpenerPolicy: {
policy: "same-origin-allow-popups", // policy: "same-origin-allow-popups",
}, // },
}), // }),
); // );
const port = process.env.PORT || 3000; const port = process.env.PORT || 3000;

View File

@ -1,176 +1,181 @@
import { useEffect } from 'react'; import { useEffect } from "react";
import { motion } from 'framer-motion'; import { motion } from "framer-motion";
import { Card } from '../UI/Card'; import { Card } from "../UI/Card";
import { ScrollDownArrow } from "../UI/ScrollDownArrow";
export default function UltraSound() { export default function UltraSound() {
useEffect(() => { useEffect(() => {
window.scrollTo({ top: 0, behavior: "smooth" }) window.scrollTo({ top: 0, behavior: "smooth" });
}, []) }, []);
const scrollToContent = () => { const scrollToContent = () => {
const contentSection = document.getElementById('video-section'); const contentSection = document.getElementById("video-section");
contentSection?.scrollIntoView({ behavior: 'smooth' }); contentSection?.scrollIntoView({ behavior: "smooth" });
}; };
const videoDetails = { const videoDetails = {
title: "Ултразвучно испитување", title: "Ултразвучно испитување",
description: "Ултразвучното испитување е неразорна метода за испитување на материјали која користи високофреквентни звучни бранови за откривање на внатрешни дефекти или за карактеризација на материјалите.", description:
benefits: [ "Ултразвучното испитување е неразорна метода за испитување на материјали која користи високофреквентни звучни бранови за откривање на внатрешни дефекти или за карактеризација на материјалите.",
"Неразорно испитување - не предизвикува оштетување на материјалот", benefits: [
"Висока прецизност во откривањето на дефекти", "Неразорно испитување - не предизвикува оштетување на материјалот",
"Можност за испитување на различни материјали и дебелини", "Висока прецизност во откривањето на дефекти",
"Брзи и точни резултати", "Можност за испитување на различни материјали и дебелини",
"Безбедна метода за операторот и околината" "Брзи и точни резултати",
], "Безбедна метода за операторот и околината",
applications: [ ],
"Испитување на заварени споеви", applications: [
"Детекција на пукнатини и празнини", "Испитување на заварени споеви",
"Мерење на дебелина на материјали", "Детекција на пукнатини и празнини",
"Контрола на квалитет во производство", "Мерење на дебелина на материјали",
"Инспекција на метални конструкции" "Контрола на квалитет во производство",
], "Инспекција на метални конструкции",
videoUrl: "https://www.youtube.com/embed/WGjG8XWS9RQ?si=fE2aL5eZ-5ivuuxV" ],
}; videoUrl: "https://www.youtube.com/embed/WGjG8XWS9RQ?si=fE2aL5eZ-5ivuuxV",
};
return ( return (
<div className="min-h-screen bg-neutral-50"> <div className="min-h-screen bg-neutral-50">
{/* Hero Section */} {/* Hero Section */}
<section className="relative bg-gradient-to-br from-primary-900 to-primary-700"> <section className="relative bg-gradient-to-br from-primary-900 to-primary-700">
<div className="absolute inset-0 bg-black/20" /> <div className="absolute inset-0 bg-black/20" />
<div className="relative container-base h-screen flex items-center justify-center"> <div className="relative container-base h-screen flex items-center justify-center">
<div className="flex flex-col items-center"> <div className="flex flex-col items-center">
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }} transition={{ duration: 0.8 }}
className="max-w-3xl mx-auto text-center" className="max-w-3xl mx-auto text-center"
> >
<h1 className="text-4xl md:text-5xl font-display font-bold text-white mb-6"> <h1 className="text-4xl md:text-5xl font-display font-bold text-white mb-6">
{videoDetails.title} {videoDetails.title}
</h1> </h1>
<div className="w-24 h-1 bg-white/30 mx-auto mb-8"></div> <div className="w-24 h-1 bg-white/30 mx-auto mb-8"></div>
<p className="text-xl text-neutral-100"> <p className="text-xl text-neutral-100">
{videoDetails.description} {videoDetails.description}
</p> </p>
</motion.div> </motion.div>
{/* Animated Down Arrow */} {/* Animated Down Arrow */}
<motion.button {/* <motion.button
onClick={scrollToContent} onClick={scrollToContent}
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ animate={{
opacity: 1, opacity: 1,
y: [0, 10, 0] y: [0, 10, 0],
}} }}
transition={{ transition={{
duration: 1.5, duration: 1.5,
repeat: Infinity, repeat: Infinity,
delay: 1 delay: 1,
}} }}
className="absolute bottom-24 cursor-pointer focus:outline-none" className="absolute bottom-24 cursor-pointer focus:outline-none"
aria-label="Scroll to content" aria-label="Scroll to content"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
className="h-12 w-12 text-white/80 hover:text-white transition-colors" className="h-12 w-12 text-white/80 hover:text-white transition-colors"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke="currentColor" stroke="currentColor"
> >
<path <path
strokeLinecap="round" strokeLinecap="round"
strokeLinejoin="round" strokeLinejoin="round"
strokeWidth={2} strokeWidth={2}
d="M19 14l-7 7m0 0l-7-7m7 7V3" d="M19 14l-7 7m0 0l-7-7m7 7V3"
/> />
</svg> </svg>
</motion.button> </motion.button> */}
</div> <div>
</div> <ScrollDownArrow />
</section>
{/* Video Section */}
<section id="video-section" className="py-24">
<div className="container-base">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="aspect-video rounded-xl overflow-hidden shadow-xl"
>
<iframe
className="w-full h-full"
src={videoDetails.videoUrl}
title="Ултразвучно испитување демонстрација"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen
/>
</motion.div>
</div>
</section>
{/* Info Section */}
<section className="py-24 bg-white">
<div className="container-base">
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5 }}
>
<Card className="h-full">
<div className="p-8">
<h2 className="text-2xl font-display font-bold text-neutral-900 mb-6">
Придобивки
</h2>
<ul className="space-y-4">
{videoDetails.benefits.map((benefit, index) => (
<motion.li
key={index}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.1 }}
className="flex items-start"
>
<span className="flex-shrink-0 w-1.5 h-1.5 mt-2 rounded-full bg-primary-600 mr-3" />
<span className="text-neutral-600">{benefit}</span>
</motion.li>
))}
</ul>
</div>
</Card>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5 }}
>
<Card className="h-full">
<div className="p-8">
<h2 className="text-2xl font-display font-bold text-neutral-900 mb-6">
Примена
</h2>
<ul className="space-y-4">
{videoDetails.applications.map((application, index) => (
<motion.li
key={index}
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.1 }}
className="flex items-start"
>
<span className="flex-shrink-0 w-1.5 h-1.5 mt-2 rounded-full bg-primary-600 mr-3" />
<span className="text-neutral-600">{application}</span>
</motion.li>
))}
</ul>
</div>
</Card>
</motion.div>
</div>
</div>
</section>
</div> </div>
); </div>
} </div>
</section>
{/* Video Section */}
<section id="video-section" className="py-24">
<div className="container-base">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="aspect-video rounded-xl overflow-hidden shadow-xl"
>
<iframe
className="w-full h-full"
src={videoDetails.videoUrl}
title="Ултразвучно испитување демонстрација"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen
/>
</motion.div>
</div>
</section>
{/* Info Section */}
<section className="py-24 bg-white">
<div className="container-base">
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5 }}
>
<Card className="h-full">
<div className="p-8">
<h2 className="text-2xl font-display font-bold text-neutral-900 mb-6">
Придобивки
</h2>
<ul className="space-y-4">
{videoDetails.benefits.map((benefit, index) => (
<motion.li
key={index}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.1 }}
className="flex items-start"
>
<span className="flex-shrink-0 w-1.5 h-1.5 mt-2 rounded-full bg-primary-600 mr-3" />
<span className="text-neutral-600">{benefit}</span>
</motion.li>
))}
</ul>
</div>
</Card>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5 }}
>
<Card className="h-full">
<div className="p-8">
<h2 className="text-2xl font-display font-bold text-neutral-900 mb-6">
Примена
</h2>
<ul className="space-y-4">
{videoDetails.applications.map((application, index) => (
<motion.li
key={index}
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.1 }}
className="flex items-start"
>
<span className="flex-shrink-0 w-1.5 h-1.5 mt-2 rounded-full bg-primary-600 mr-3" />
<span className="text-neutral-600">{application}</span>
</motion.li>
))}
</ul>
</div>
</Card>
</motion.div>
</div>
</div>
</section>
</div>
);
}

View File

@ -25,7 +25,7 @@ const serviceCards = [
image: "wallscener.jpeg", image: "wallscener.jpeg",
link: "/ultrasound", link: "/ultrasound",
services: [ services: [
"Испитување на заварени споеви", "Испитувања со ултразвук",
"Дебелометрија", "Дебелометрија",
"Испитување на дефекти", "Испитување на дефекти",
"Контрола на квалитет", "Контрола на квалитет",

View File

@ -5,13 +5,13 @@ export default defineConfig({
plugins: [react()], plugins: [react()],
server: { server: {
port: 5173, port: 5173,
cors: { // cors: {
origin: [ // origin: [
"https://eu-assets.i.posthog.com", // "https://eu-assets.i.posthog.com",
"https://app.posthog.com", // "https://app.posthog.com",
"https://eu.posthog.com", // "https://eu.posthog.com",
], // ],
allowedHeaders: ["posthog-session-id", "ph-session-id"], // allowedHeaders: ["posthog-session-id", "ph-session-id"],
}, // },
}, },
}); });