forgot password fix
This commit is contained in:
parent
88f20bbb3b
commit
800a2d3bdd
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,4 +9,5 @@ frontend/.vite
|
||||
node_modules
|
||||
pestgres_data/
|
||||
redis_data/
|
||||
frontend-rewrite
|
||||
aisugestions.md
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
export const corsConfig = {
|
||||
origin: [
|
||||
"http://localhost:5173",
|
||||
"http://localhost:4444",
|
||||
"http://localhost:3000",
|
||||
"https://www.placebo.mk",
|
||||
"https://placebo.mk",
|
||||
"https://imkapi.oblak.solutions",
|
||||
|
||||
@ -227,8 +227,9 @@ export class EmailService {
|
||||
|
||||
async sendPasswordResetEmail(email: string, name: string, token: string) {
|
||||
this.logger.log("Sending password reset email to:", email);
|
||||
|
||||
const resetLink = `${process.env.FRONTEND_URL}/reset-password?token=${token}`;
|
||||
const frontendUrl =
|
||||
this.configService.get<string>("FRONTEND_URL") || "https://placebo.mk";
|
||||
const resetLink = `${frontendUrl}/reset-password?token=${token}`;
|
||||
const mailOptions = {
|
||||
from: this.from,
|
||||
to: email,
|
||||
|
||||
@ -1,31 +1,35 @@
|
||||
# Stop everything
|
||||
|
||||
docker-compose down
|
||||
|
||||
# Remove all containers
|
||||
|
||||
docker rm -f $(docker ps -a -q)
|
||||
|
||||
# Remove all volumes
|
||||
|
||||
docker volume rm $(docker volume ls -q)
|
||||
|
||||
# Remove all images
|
||||
|
||||
docker rmi -f $(docker images -q)
|
||||
|
||||
# Start fresh
|
||||
|
||||
docker-compose up --build
|
||||
|
||||
|
||||
|
||||
# reset development
|
||||
|
||||
# Stop containers and remove volumes
|
||||
|
||||
docker-compose -f docker-compose.dev.yml down -v
|
||||
|
||||
# Remove existing images
|
||||
|
||||
docker rmi $(docker images -q backend_backend)
|
||||
|
||||
# Rebuild and start
|
||||
|
||||
docker-compose -f docker-compose.dev.yml up --build
|
||||
|
||||
|
||||
|
||||
|
||||
docker-compose -f docker-compose.dev.yml up --build --force-recreate
|
||||
|
||||
BIN
frontend/public/Sertifikat za akreditacija IMK.png
Normal file
BIN
frontend/public/Sertifikat za akreditacija IMK.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 MiB |
BIN
frontend/public/resenie_imk.png
Normal file
BIN
frontend/public/resenie_imk.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 612 KiB |
@ -1,103 +1,161 @@
|
||||
import { useEffect } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Card } from '../UI/Card';
|
||||
import { ScrollDownArrow } from '../UI/ScrollDownArrow';
|
||||
import { useEffect, useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { Card } from "../UI/Card";
|
||||
import { ScrollDownArrow } from "../UI/ScrollDownArrow";
|
||||
|
||||
export default function Certificates() {
|
||||
useEffect(() => {
|
||||
// Ensure the scroll happens after the component is fully mounted
|
||||
const timer = setTimeout(() => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: "smooth"
|
||||
});
|
||||
}, 100);
|
||||
useEffect(() => {
|
||||
// Ensure the scroll happens after the component is fully mounted
|
||||
const timer = setTimeout(() => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}, 100);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const certificates = [
|
||||
{
|
||||
id: 1,
|
||||
image: "/sertifikat1.jpg",
|
||||
alt: "Сертификат за акредитација",
|
||||
title: "Сертификат за акредитација",
|
||||
description: "Официјален сертификат за акредитација на нашата лабораторија"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
image: "/sertifikat2.jpg",
|
||||
alt: "Сертификат за квалитет",
|
||||
title: "Сертификат за квалитет",
|
||||
description: "Потврда за нашиот систем за управување со квалитет"
|
||||
}
|
||||
];
|
||||
const [selectedCertificate, setSelectedCertificate] = useState(null);
|
||||
const certificates = [
|
||||
{
|
||||
id: 1,
|
||||
image: "/sertifikat1.jpg",
|
||||
alt: "Сертификат за акредитација",
|
||||
title: "Сертификат за акредитација",
|
||||
description:
|
||||
"Официјален сертификат за акредитација на нашата лабораторија",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
image: "/sertifikat2.jpg",
|
||||
alt: "Сертификат за квалитет",
|
||||
title: "Сертификат за квалитет",
|
||||
description: "Потврда за нашиот систем за управување со квалитет",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
image: "/resenie_imk.png",
|
||||
alt: "resenie ",
|
||||
title: "resenie",
|
||||
description: "some description",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
alt: "Сертификат",
|
||||
image: "/Sertifikat za akreditacija IMK.png",
|
||||
title: "Сертификат за акредитација",
|
||||
description: "Официјален сертификат за акредитација",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-neutral-50">
|
||||
{/* Hero Section */}
|
||||
<section className="relative bg-gradient-to-br from-primary-900 to-primary-700">
|
||||
<div className="absolute inset-0 bg-black/20" />
|
||||
<div className="relative container-base h-screen flex items-center justify-center">
|
||||
<div className="flex flex-col items-center">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="max-w-3xl mx-auto text-center"
|
||||
>
|
||||
<h1 className="text-4xl md:text-5xl font-display font-bold text-white mb-6">
|
||||
Сертификати и Акредитации
|
||||
</h1>
|
||||
<div className="w-24 h-1 bg-white/30 mx-auto mb-8"></div>
|
||||
<p className="text-xl text-neutral-100">
|
||||
Нашата посветеност кон квалитет и професионалност е потврдена со меѓународно признати сертификати
|
||||
</p>
|
||||
</motion.div>
|
||||
const handleCertificateClick = (certificate) => {
|
||||
setSelectedCertificate(certificate);
|
||||
};
|
||||
|
||||
<ScrollDownArrow targetId="certificates-section" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
const closeModal = () => {
|
||||
setSelectedCertificate(null);
|
||||
};
|
||||
return (
|
||||
<div className="min-h-screen bg-neutral-50">
|
||||
{/* Hero Section */}
|
||||
<section className="relative bg-gradient-to-br from-primary-900 to-primary-700">
|
||||
<div className="absolute inset-0 bg-black/20" />
|
||||
<div className="relative container-base h-screen flex items-center justify-center">
|
||||
<div className="flex flex-col items-center">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="max-w-3xl mx-auto text-center"
|
||||
>
|
||||
<h1 className="text-4xl md:text-5xl font-display font-bold text-white mb-6">
|
||||
Сертификати и Акредитации
|
||||
</h1>
|
||||
<div className="w-24 h-1 bg-white/30 mx-auto mb-8"></div>
|
||||
<p className="text-xl text-neutral-100">
|
||||
Нашата посветеност кон квалитет и професионалност е потврдена со
|
||||
меѓународно признати сертификати
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Certificates Section */}
|
||||
<section id="certificates-section" className="py-24">
|
||||
<div className="container-base">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-12">
|
||||
{certificates.map((certificate, index) => (
|
||||
<motion.div
|
||||
key={certificate.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: index * 0.2 }}
|
||||
>
|
||||
<Card className="h-full overflow-hidden hover:shadow-xl transition-all duration-300">
|
||||
<motion.div
|
||||
whileHover={{ scale: 1.02 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="relative aspect-[4/3] overflow-hidden"
|
||||
>
|
||||
<img
|
||||
src={certificate.image}
|
||||
alt={certificate.alt}
|
||||
className="w-full h-full object-cover"
|
||||
loading="lazy"
|
||||
/>
|
||||
</motion.div>
|
||||
<div className="p-6">
|
||||
<h2 className="text-xl font-display font-semibold text-neutral-900 mb-2">
|
||||
{certificate.title}
|
||||
</h2>
|
||||
<p className="text-neutral-600">
|
||||
{certificate.description}
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<ScrollDownArrow targetId="certificates-section" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</section>
|
||||
|
||||
{/* Certificates Section */}
|
||||
<section id="certificates-section" className="py-24">
|
||||
<div className="container-base">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-12">
|
||||
{certificates.map((certificate, index) => (
|
||||
<motion.div
|
||||
key={certificate.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: index * 0.2 }}
|
||||
onClick={() => handleCertificateClick(certificate)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Card className="h-full overflow-hidden hover:shadow-xl transition-all duration-300">
|
||||
<motion.div
|
||||
whileHover={{ scale: 1.02 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="relative aspect-[4/3] overflow-hidden"
|
||||
>
|
||||
<img
|
||||
src={certificate.image}
|
||||
alt={certificate.alt}
|
||||
className="w-full h-full object-cover"
|
||||
loading="lazy"
|
||||
/>
|
||||
</motion.div>
|
||||
<div className="p-6">
|
||||
<h2 className="text-xl font-display font-semibold text-neutral-900 mb-2">
|
||||
{certificate.title}
|
||||
</h2>
|
||||
<p className="text-neutral-600">
|
||||
{certificate.description}
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{selectedCertificate && (
|
||||
<div
|
||||
className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
|
||||
onClick={closeModal}
|
||||
>
|
||||
{/* <div className="relative bg-white p-6 rounded-lg shadow-lg max-w-3xl w-full"> */}
|
||||
{/* <button */}
|
||||
{/* onClick={closeModal} */}
|
||||
{/* className="absolute top-2 right-2 text-gray-500 hover:text-gray-800" */}
|
||||
{/* > */}
|
||||
{/* ✕ */}
|
||||
{/* </button> */}
|
||||
<div className="">
|
||||
<motion.img
|
||||
src={selectedCertificate.image}
|
||||
alt={selectedCertificate.alt}
|
||||
className="w-full h-screen mx-auto object-contain"
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
/>
|
||||
{/* <h2 className="text-xl font-display font-semibold text-neutral-900 mt-4"> */}
|
||||
{/* {selectedCertificate.title} */}
|
||||
{/* </h2> */}
|
||||
{/* <p className="text-neutral-600 mt-2"> */}
|
||||
{/* {selectedCertificate.description} */}
|
||||
{/* </p> */}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,41 +1,42 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
export function ScrollDownArrow({ targetId = 'content' }) {
|
||||
const scrollToContent = () => {
|
||||
const contentSection = document.getElementById(targetId);
|
||||
contentSection?.scrollIntoView({ behavior: 'smooth' });
|
||||
};
|
||||
export function ScrollDownArrow({ targetId = "content" }) {
|
||||
const scrollToContent = () => {
|
||||
const contentSection = document.getElementById(targetId);
|
||||
contentSection?.scrollIntoView({ behavior: "smooth" });
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.button
|
||||
onClick={scrollToContent}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
y: [0, 10, 0],
|
||||
}}
|
||||
transition={{
|
||||
duration: 1.5,
|
||||
repeat: Infinity,
|
||||
delay: 1,
|
||||
}}
|
||||
className="absolute bottom-36 cursor-pointer focus:outline-none"
|
||||
aria-label="Scroll to content"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-12 w-12 text-white/80 hover:text-white transition-colors"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 14l-7 7m0 0l-7-7m7 7V3"
|
||||
/>
|
||||
</svg>
|
||||
</motion.button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.button
|
||||
onClick={scrollToContent}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
y: [0, 10, 0]
|
||||
}}
|
||||
transition={{
|
||||
duration: 1.5,
|
||||
repeat: Infinity,
|
||||
delay: 1
|
||||
}}
|
||||
className="absolute bottom-24 cursor-pointer focus:outline-none"
|
||||
aria-label="Scroll to content"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-12 w-12 text-white/80 hover:text-white transition-colors"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 14l-7 7m0 0l-7-7m7 7V3"
|
||||
/>
|
||||
</svg>
|
||||
</motion.button>
|
||||
);
|
||||
}
|
||||
@ -48,11 +48,9 @@ export default function Login() {
|
||||
>
|
||||
<div className="text-center mb-8">
|
||||
<h2 className="text-2xl font-semibold text-white">
|
||||
Sign in to your account
|
||||
Логирај се на својот акаунт
|
||||
</h2>
|
||||
<p className="text-sm text-neutral-300 mt-2">
|
||||
Welcome back! Please enter your details.
|
||||
</p>
|
||||
<p className="text-sm text-neutral-300 mt-2">Добредојдовте!</p>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
@ -64,19 +62,19 @@ export default function Login() {
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<FormGroup>
|
||||
<Label htmlFor="username" className="text-white">
|
||||
Username
|
||||
Е мејл
|
||||
</Label>
|
||||
<Input
|
||||
id="username"
|
||||
type="text"
|
||||
required
|
||||
placeholder="Enter your username"
|
||||
placeholder="Внесете го вашиот мејл"
|
||||
value={formData.username}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, username: e.target.value })
|
||||
}
|
||||
error={error}
|
||||
className="bg-neutral-800/50 border-neutral-700 text-white placeholder-neutral-400
|
||||
className="p-2 bg-neutral-800/50 border-neutral-700 text-white placeholder-neutral-400
|
||||
focus:border-primary-400 focus:ring-primary-400"
|
||||
/>
|
||||
</FormGroup>
|
||||
@ -84,7 +82,7 @@ export default function Login() {
|
||||
<FormGroup>
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="password" className="text-white">
|
||||
Password
|
||||
Лозинка
|
||||
</Label>
|
||||
{/* <Button
|
||||
variant="ghost"
|
||||
@ -99,13 +97,13 @@ export default function Login() {
|
||||
id="password"
|
||||
type="password"
|
||||
required
|
||||
placeholder="Enter your password"
|
||||
placeholder="Внесете ја вашата лозинка"
|
||||
value={formData.password}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, password: e.target.value })
|
||||
}
|
||||
error={error}
|
||||
className="bg-neutral-800/50 border-neutral-700 text-white placeholder-neutral-400
|
||||
className="p-2 bg-neutral-800/50 border-neutral-700 text-white placeholder-neutral-400
|
||||
focus:border-primary-400 focus:ring-primary-400"
|
||||
/>
|
||||
</FormGroup>
|
||||
@ -130,7 +128,7 @@ export default function Login() {
|
||||
to="/forgot-password"
|
||||
className="font-medium text-primary-400 hover:text-primary-300 transition-colors"
|
||||
>
|
||||
Forgot your password?
|
||||
Ја заборавивте вашата лозинка?
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -54,7 +54,7 @@ export default function Navbar() {
|
||||
<div className="flex items-center justify-between">
|
||||
{/* Left Logo */}
|
||||
<div className="flex-shrink-0">
|
||||
<Link to="/" className="p-2 hover:bg-white/20 transition-colors">
|
||||
<Link to="/" className="p-2">
|
||||
<img
|
||||
className="h-10 w-auto transition-transform duration-300 hover:scale-105"
|
||||
src="/imklogorgb.png"
|
||||
@ -96,23 +96,22 @@ export default function Navbar() {
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={handleLogout}
|
||||
className="text-white hover:bg-primary-700/50 px-4 py-2 text-sm"
|
||||
className="text-white border border-red-400 hover:bg-primary-700/50 px-4 py-2 text-sm"
|
||||
>
|
||||
Одјави се
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TUF Logo */}
|
||||
<div className="hidden lg:block">
|
||||
<a href="/" className="p-2 hover:bg-white/20 transition-colors">
|
||||
<img
|
||||
className="h-10 w-auto transition-transform duration-300 hover:scale-105"
|
||||
src="/tuf.png"
|
||||
alt="TUF logo"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
{/* <div className="hidden lg:block"> */}
|
||||
{/* <a href="/" className="p-2 hover:bg-white/20 transition-colors"> */}
|
||||
{/* <img */}
|
||||
{/* className="h-10 w-auto transition-transform duration-300 hover:scale-105" */}
|
||||
{/* src="/tuf.png" */}
|
||||
{/* alt="TUF logo" */}
|
||||
{/* /> */}
|
||||
{/* </a> */}
|
||||
{/* </div> */}
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<Button
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { motion } from "framer-motion";
|
||||
import { FiTrendingUp, FiAward, FiCheckCircle } from "react-icons/fi";
|
||||
import { Button, Card } from "../../components/UI";
|
||||
import { ScrollDownArrow } from '../../components/UI/ScrollDownArrow';
|
||||
import { ScrollDownArrow } from "../../components/UI/ScrollDownArrow";
|
||||
|
||||
const milestones = [
|
||||
{ year: "2008", title: "Основање на компанијата" },
|
||||
@ -15,7 +15,8 @@ const milestones = [
|
||||
const values = [
|
||||
{
|
||||
title: "Квалитет",
|
||||
description: "Посветени сме на обезбедување највисок квалитет во сите наши услуги",
|
||||
description:
|
||||
"Посветени сме на обезбедување највисок квалитет во сите наши услуги",
|
||||
color: "text-primary-500",
|
||||
},
|
||||
{
|
||||
@ -105,15 +106,21 @@ export default function About() {
|
||||
<Card className="bg-gradient-to-br from-primary-800 to-primary-600 text-white">
|
||||
<div className="flex items-center mb-6">
|
||||
<FiAward className="w-8 h-8 mr-4" />
|
||||
<h3 className="text-2xl font-display font-semibold">Вредности</h3>
|
||||
<h3 className="text-2xl font-display font-semibold">
|
||||
Вредности
|
||||
</h3>
|
||||
</div>
|
||||
<ul className="space-y-4">
|
||||
{values.map((value, index) => (
|
||||
<li key={index} className="flex items-start">
|
||||
<FiCheckCircle className="w-5 h-5 mr-3 mt-1" />
|
||||
<div>
|
||||
<strong className="block text-lg mb-1">{value.title}</strong>
|
||||
<span className="text-neutral-100">{value.description}</span>
|
||||
<strong className="block text-lg mb-1">
|
||||
{value.title}
|
||||
</strong>
|
||||
<span className="text-neutral-100">
|
||||
{value.description}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
@ -124,35 +131,35 @@ export default function About() {
|
||||
</section>
|
||||
|
||||
{/* Milestones Section */}
|
||||
<section className="py-24 bg-white">
|
||||
<div className="container-base">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl md:text-4xl font-display font-bold text-neutral-900 mb-4">
|
||||
Нашиот Пат
|
||||
</h2>
|
||||
<div className="w-24 h-1 bg-primary-500 mx-auto mb-6"></div>
|
||||
<p className="text-lg text-neutral-600 max-w-2xl mx-auto">
|
||||
Клучни моменти во нашиот развој
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{milestones.map((milestone, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
className="text-center hover:-translate-y-1 transition-transform duration-300"
|
||||
>
|
||||
<div className="text-primary-600 text-3xl font-bold mb-2">
|
||||
{milestone.year}
|
||||
</div>
|
||||
<div className="text-neutral-800 text-lg font-medium">
|
||||
{milestone.title}
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/* <section className="py-24 bg-white"> */}
|
||||
{/* <div className="container-base"> */}
|
||||
{/* <div className="text-center mb-16"> */}
|
||||
{/* <h2 className="text-3xl md:text-4xl font-display font-bold text-neutral-900 mb-4"> */}
|
||||
{/* Нашиот Пат */}
|
||||
{/* </h2> */}
|
||||
{/* <div className="w-24 h-1 bg-primary-500 mx-auto mb-6"></div> */}
|
||||
{/* <p className="text-lg text-neutral-600 max-w-2xl mx-auto"> */}
|
||||
{/* Клучни моменти во нашиот развој */}
|
||||
{/* </p> */}
|
||||
{/* </div> */}
|
||||
{/**/}
|
||||
{/* <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"> */}
|
||||
{/* {milestones.map((milestone, index) => ( */}
|
||||
{/* <Card */}
|
||||
{/* key={index} */}
|
||||
{/* className="text-center hover:-translate-y-1 transition-transform duration-300" */}
|
||||
{/* > */}
|
||||
{/* <div className="text-primary-600 text-3xl font-bold mb-2"> */}
|
||||
{/* {milestone.year} */}
|
||||
{/* </div> */}
|
||||
{/* <div className="text-neutral-800 text-lg font-medium"> */}
|
||||
{/* {milestone.title} */}
|
||||
{/* </div> */}
|
||||
{/* </Card> */}
|
||||
{/* ))} */}
|
||||
{/* </div> */}
|
||||
{/* </div> */}
|
||||
{/* </section> */}
|
||||
|
||||
{/* CTA Section */}
|
||||
<section className="py-24 bg-gradient-to-br from-primary-900 to-primary-700">
|
||||
|
||||
@ -1,89 +1,98 @@
|
||||
import { useState } from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import {
|
||||
EnvelopeIcon,
|
||||
PhoneIcon,
|
||||
MapPinIcon
|
||||
} from '@heroicons/react/24/outline'
|
||||
import { Button, Card, Input } from '../../components/UI'
|
||||
import { Switch } from '@headlessui/react'
|
||||
import { useState, useEffect } from "react";
|
||||
import {
|
||||
EnvelopeIcon,
|
||||
PhoneIcon,
|
||||
MapPinIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Switch } from "@headlessui/react";
|
||||
|
||||
const contactInfo = [
|
||||
{
|
||||
icon: PhoneIcon,
|
||||
title: 'Телефон',
|
||||
content: '+389 XX XXX XXX',
|
||||
link: 'tel:+389XXXXXXX'
|
||||
title: "Телефон",
|
||||
content: "+389 XX XXX XXX",
|
||||
link: "tel:+389XXXXXXX",
|
||||
},
|
||||
{
|
||||
icon: EnvelopeIcon,
|
||||
title: 'Емаил',
|
||||
content: 'contact@imk.mk',
|
||||
link: 'mailto:contact@imk.mk'
|
||||
title: "Емаил",
|
||||
content: "contact@imk.mk",
|
||||
link: "mailto:contact@imk.mk",
|
||||
},
|
||||
{
|
||||
icon: MapPinIcon,
|
||||
title: 'Адреса',
|
||||
content: 'Скопје, Македонија',
|
||||
link: 'https://goo.gl/maps/your-location'
|
||||
}
|
||||
]
|
||||
title: "Адреса",
|
||||
content: "Скопје, Македонија",
|
||||
link: "https://goo.gl/maps/your-location",
|
||||
},
|
||||
];
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
export default function Contact() {
|
||||
useEffect(() => {
|
||||
// Ensure the scroll happens after the component is fully mounted
|
||||
const timer = setTimeout(() => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}, 100);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
const [formData, setFormData] = useState({
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
company: '',
|
||||
email: '',
|
||||
message: ''
|
||||
})
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
const [submitStatus, setSubmitStatus] = useState(null)
|
||||
const [agreed, setAgreed] = useState(false)
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
company: "",
|
||||
email: "",
|
||||
message: "",
|
||||
});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [submitStatus, setSubmitStatus] = useState(null);
|
||||
const [agreed, setAgreed] = useState(false);
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault()
|
||||
setIsSubmitting(true)
|
||||
|
||||
e.preventDefault();
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
const response = await fetch('https://formsubmit.co/taratur@gmail.com', {
|
||||
method: 'POST',
|
||||
const response = await fetch("https://formsubmit.co/taratur@gmail.com", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(formData)
|
||||
})
|
||||
body: JSON.stringify(formData),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
setSubmitStatus('success')
|
||||
setSubmitStatus("success");
|
||||
setFormData({
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
company: '',
|
||||
email: '',
|
||||
message: ''
|
||||
})
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
company: "",
|
||||
email: "",
|
||||
message: "",
|
||||
});
|
||||
} else {
|
||||
setSubmitStatus('error')
|
||||
setSubmitStatus("error");
|
||||
}
|
||||
} catch (error) {
|
||||
setSubmitStatus('error')
|
||||
setSubmitStatus("error");
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target
|
||||
setFormData(prev => ({
|
||||
const { name, value } = e.target;
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
}))
|
||||
}
|
||||
[name]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="isolate px-6 bg-gradient-to-b from-primary-900 to-primary-700 sm:py-32 lg:px-8 h-screen">
|
||||
@ -92,14 +101,11 @@ export default function Contact() {
|
||||
Пишете ни порака | Закажете средба
|
||||
</h2>
|
||||
</div>
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="mx-auto mt-16 max-w-xl sm:mt-20"
|
||||
>
|
||||
<form onSubmit={handleSubmit} className="mx-auto mt-16 max-w-xl sm:mt-20">
|
||||
<div className="grid grid-cols-1 gap-x-8 gap-y-6 sm:grid-cols-2">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="first-name"
|
||||
<label
|
||||
htmlFor="first-name"
|
||||
className="block text-sm font-semibold leading-6 text-white"
|
||||
>
|
||||
Име
|
||||
@ -120,8 +126,8 @@ export default function Contact() {
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="last-name"
|
||||
<label
|
||||
htmlFor="last-name"
|
||||
className="block text-sm font-semibold leading-6 text-white"
|
||||
>
|
||||
Презиме
|
||||
@ -142,8 +148,8 @@ export default function Contact() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="sm:col-span-2">
|
||||
<label
|
||||
htmlFor="company"
|
||||
<label
|
||||
htmlFor="company"
|
||||
className="block text-sm font-semibold leading-6 text-white"
|
||||
>
|
||||
Компанија
|
||||
@ -164,8 +170,8 @@ export default function Contact() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="sm:col-span-2">
|
||||
<label
|
||||
htmlFor="email"
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-sm font-semibold leading-6 text-white"
|
||||
>
|
||||
Мејл
|
||||
@ -187,8 +193,8 @@ export default function Contact() {
|
||||
</div>
|
||||
|
||||
<div className="sm:col-span-2">
|
||||
<label
|
||||
htmlFor="message"
|
||||
<label
|
||||
htmlFor="message"
|
||||
className="block text-sm font-semibold leading-6 text-white"
|
||||
>
|
||||
Порака
|
||||
@ -207,8 +213,10 @@ export default function Contact() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Switch.Group as="div" className="flex gap-x-4 sm:col-span-2">
|
||||
</Switch.Group>
|
||||
<Switch.Group
|
||||
as="div"
|
||||
className="flex gap-x-4 sm:col-span-2"
|
||||
></Switch.Group>
|
||||
</div>
|
||||
<input type="hidden" name="_next" value="https://imk.mk/"></input>
|
||||
<div className="mt-10">
|
||||
@ -224,5 +232,6 @@ export default function Contact() {
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import { Link } from "react-router-dom";
|
||||
import { motion } from "framer-motion";
|
||||
import { ArrowRightIcon, CheckCircleIcon } from "@heroicons/react/24/outline";
|
||||
import { Button, Card } from "../../components/UI";
|
||||
import { ScrollDownArrow } from '../../components/UI/ScrollDownArrow';
|
||||
import { ScrollDownArrow } from "../../components/UI/ScrollDownArrow";
|
||||
|
||||
const serviceCards = [
|
||||
{
|
||||
@ -109,7 +109,7 @@ export default function Home() {
|
||||
{/* Services Section */}
|
||||
<section id="services-section" className="bg-neutral-50 py-24 h-screen">
|
||||
<div className="container-base">
|
||||
<div className="text-center mb-16">
|
||||
<div className="text-centeSr mb-16">
|
||||
<h2 className="text-3xl md:text-4xl font-display font-bold text-neutral-900 mb-4">
|
||||
Нашите Услуги
|
||||
</h2>
|
||||
@ -121,11 +121,7 @@ export default function Home() {
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{serviceCards.map((card) => (
|
||||
<Link
|
||||
key={card.title}
|
||||
to={card.link}
|
||||
className="group"
|
||||
>
|
||||
<Link key={card.title} to={card.link} className="group">
|
||||
<Card
|
||||
className="h-full hover:-translate-y-1 transition-all duration-300"
|
||||
padding="none"
|
||||
@ -145,7 +141,10 @@ export default function Home() {
|
||||
<p className="text-neutral-600 mb-4">{card.description}</p>
|
||||
<ul className="space-y-2">
|
||||
{card.services.slice(0, 3).map((service, index) => (
|
||||
<li key={index} className="flex items-center text-neutral-700">
|
||||
<li
|
||||
key={index}
|
||||
className="flex items-center text-neutral-700"
|
||||
>
|
||||
<CheckCircleIcon className="h-5 w-5 text-primary-500 mr-2" />
|
||||
{service}
|
||||
</li>
|
||||
@ -162,9 +161,6 @@ export default function Home() {
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import axios from "axios";
|
||||
|
||||
// const API_URL = "https://imkapi.oblak.solutions:3000";
|
||||
const API_URL = "https://localhost:3000";
|
||||
const API_URL = "http://localhost:3000";
|
||||
const api = axios.create({
|
||||
baseURL: API_URL,
|
||||
withCredentials: true,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user