frontend redesign, implementing tailwind accross app, added shared component

This commit is contained in:
Dimitar765 2024-10-29 12:30:33 +01:00
parent 1a7f2afd79
commit ce893a4185
15 changed files with 1030 additions and 570 deletions

View File

@ -126,12 +126,36 @@ export class AdminService {
}); });
} }
async createUser(createUserDto: CreateUserDto) { // async createUser(createUserDto: CreateUserDto) {
const hashedPassword = await bcrypt.hash(createUserDto.password, 10); // const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
// return this.prisma.user.create({
// data: {
// ...createUserDto,
// password: hashedPassword,
// },
// });
// }
async createUser(userData: {
name: string;
email: string;
password: string;
isAdmin: boolean;
}) {
const hashedPassword = await bcrypt.hash(userData.password, 10);
return this.prisma.user.create({ return this.prisma.user.create({
data: { data: {
...createUserDto, name: userData.name,
email: userData.email,
password: hashedPassword, password: hashedPassword,
isAdmin: userData.isAdmin,
},
select: {
id: true,
name: true,
email: true,
isAdmin: true,
}, },
}); });
} }

View File

@ -1,4 +1,4 @@
import { IsString, IsEmail, MinLength } from 'class-validator'; import { IsString, IsEmail, MinLength, IsBoolean } from 'class-validator';
export class CreateUserDto { export class CreateUserDto {
@IsString() @IsString()
@ -10,4 +10,7 @@ export class CreateUserDto {
@IsString() @IsString()
@MinLength(6) @MinLength(6)
password: string; password: string;
@IsBoolean()
isAdmin: boolean = false;
} }

98
frontend/colorScheme Normal file
View File

@ -0,0 +1,98 @@
##color scheme across all components based on the Home page design.
--
We can create a colors file to maintain consistency
color.js
export const colors = {
primary: {
light: '#3B82F6', // blue-500
DEFAULT: '#2563EB', // blue-600
dark: '#1D4ED8', // blue-700
darker: '#1E40AF' // blue-800
},
background: {
light: '#F9FAFB', // gray-50
DEFAULT: '#F3F4F6', // gray-100
dark: '#E5E7EB' // gray-200
},
text: {
primary: '#1F2937', // gray-800
secondary: '#4B5563', // gray-600
light: '#9CA3AF' // gray-400
},
white: '#FFFFFF',
black: '#000000'
}
--
--
common component style
common.js
export const commonStyles = {
// Container
container: "container mx-auto px-4",
// Section Headers
sectionHeader: "text-center mb-16",
sectionTitle: "text-3xl md:text-4xl font-bold text-gray-800 mb-4",
sectionDivider: "w-24 h-1 bg-blue-600 mx-auto",
// Cards
card: "bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300",
// Buttons
primaryButton: "bg-blue-600 hover:bg-blue-700 text-white px-8 py-3 rounded-full font-semibold transition-colors duration-300",
secondaryButton: "bg-white/10 hover:bg-white/20 text-white px-8 py-3 rounded-full font-semibold transition-colors duration-300",
// Gradients
primaryGradient: "bg-gradient-to-r from-blue-900/90 to-blue-600/80",
// Text
heading1: "text-4xl md:text-6xl font-bold",
heading2: "text-3xl md:text-4xl font-bold",
heading3: "text-2xl font-semibold",
paragraph: "text-gray-600"
}
--
--
reusable section header component
SectionHeader.jsx
// Reusable section header component
export function SectionHeader({ title, subtitle }) {
return (
<div className="text-center mb-16">
<h2 className="text-3xl md:text-4xl font-bold text-gray-800 mb-4">
{title}
</h2>
<div className="w-24 h-1 bg-blue-600 mx-auto mb-8"></div>
{subtitle && (
<p className="text-gray-600 max-w-2xl mx-auto">
{subtitle}
</p>
)}
</div>
);
}
--
reusable button component
// Reusable button component
export function Button({ variant = 'primary', children, ...props }) {
const baseStyles = "px-8 py-3 rounded-full font-semibold transition-colors duration-300";
const variants = {
primary: "bg-blue-600 hover:bg-blue-700 text-white",
secondary: "bg-white/10 hover:bg-white/20 text-white",
outline: "border-2 border-blue-600 text-blue-600 hover:bg-blue-50"
};
return (
<button
className={`${baseStyles} ${variants[variant]}`}
{...props}
>
{children}
</button>
);
}
--

View File

@ -11,6 +11,7 @@
"@headlessui/react": "^1.7.17", "@headlessui/react": "^1.7.17",
"@heroicons/react": "^2.0.18", "@heroicons/react": "^2.0.18",
"axios": "^1.7.7", "axios": "^1.7.7",
"framer-motion": "^11.11.10",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@ -2160,6 +2161,30 @@
"url": "https://github.com/sponsors/rawify" "url": "https://github.com/sponsors/rawify"
} }
}, },
"node_modules/framer-motion": {
"version": "11.11.10",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.11.10.tgz",
"integrity": "sha512-061Bt1jL/vIm+diYIiA4dP/Yld7vD47ROextS7ESBW5hr4wQFhxB5D5T5zAc3c/5me3cOa+iO5LqhA38WDln/A==",
"dependencies": {
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fs.realpath": { "node_modules/fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -4105,6 +4130,11 @@
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
"dev": true "dev": true
}, },
"node_modules/tslib": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz",
"integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA=="
},
"node_modules/type-check": { "node_modules/type-check": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",

View File

@ -13,6 +13,7 @@
"@headlessui/react": "^1.7.17", "@headlessui/react": "^1.7.17",
"@heroicons/react": "^2.0.18", "@heroicons/react": "^2.0.18",
"axios": "^1.7.7", "axios": "^1.7.7",
"framer-motion": "^11.11.10",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",

View File

@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { getAllUsers, getAllDocuments, getUserInfo } from '../../services/api'; import { getAllUsers, getAllDocuments, getUserInfo, createUser } from '../../services/api';
import DocumentUpload from '../documentUpload/DocumentUpload'; import DocumentUpload from '../documentUpload/DocumentUpload';
import UserCreate from '../UserCreate'; //import UserCreate from '../UserCreate';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
function AdminPanel() { function AdminPanel() {
@ -12,6 +12,12 @@ function AdminPanel() {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState(''); const [error, setError] = useState('');
const [isAdmin, setIsAdmin] = useState(false); const [isAdmin, setIsAdmin] = useState(false);
const [newUser, setNewUser] = useState({
name: '',
email: '',
password: '',
isAdmin: false
});
useEffect(() => { useEffect(() => {
checkAdminStatus(); checkAdminStatus();
@ -36,6 +42,7 @@ function AdminPanel() {
if (isAdmin) { if (isAdmin) {
fetchData(); fetchData();
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeTab, isAdmin]); }, [activeTab, isAdmin]);
const fetchData = async () => { const fetchData = async () => {
@ -70,6 +77,23 @@ function AdminPanel() {
return null; return null;
} }
const handleCreateUser = async (e) => {
e.preventDefault();
try {
await createUser(newUser);
setNewUser({
name: '',
email: '',
password: '',
isAdmin: false // Reset isAdmin too
});
fetchData();
} catch (err) {
setError('Failed to create user');
}
};
return ( return (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="bg-white rounded-lg shadow"> <div className="bg-white rounded-lg shadow">
@ -165,37 +189,78 @@ function AdminPanel() {
)} )}
{activeTab === 'users' && ( {activeTab === 'users' && (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> <>
<div> <form onSubmit={handleCreateUser} className="mb-8">
<div className="overflow-x-auto"> <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-4">
<table className="min-w-full divide-y divide-gray-200"> <input
<thead className="bg-gray-50"> type="text"
<tr> placeholder="Name"
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th> value={newUser.name}
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th> onChange={(e) => setNewUser({ ...newUser, name: e.target.value })}
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Role</th> className="rounded border p-2"
</tr> />
</thead> <input
<tbody className="bg-white divide-y divide-gray-200"> type="email"
{users.map((user) => ( placeholder="Email"
<tr key={user.id}> value={newUser.email}
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{user.name}</td> onChange={(e) => setNewUser({ ...newUser, email: e.target.value })}
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{user.email}</td> className="rounded border p-2"
<td className="px-6 py-4 whitespace-nowrap"> />
<span className={`px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${user.isAdmin ? 'bg-purple-100 text-purple-800' : 'bg-gray-100 text-gray-800'}`}> <input
{user.isAdmin ? 'Admin' : 'User'} type="password"
</span> placeholder="Password"
</td> value={newUser.password}
</tr> onChange={(e) => setNewUser({ ...newUser, password: e.target.value })}
))} className="rounded border p-2"
</tbody> />
</table> <div className="flex items-center">
</div> <input
</div> type="checkbox"
<div> id="isAdmin"
<UserCreate onUserCreated={fetchData} /> checked={newUser.isAdmin}
</div> onChange={(e) => setNewUser({ ...newUser, isAdmin: e.target.checked })}
</div> className="mr-2"
/>
<label htmlFor="isAdmin">Is Admin</label>
</div>
<button
type="submit"
className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
>
Create User
</button>
</div>
</form>
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Admin Status</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{users.map((user) => (
<tr key={user.id}>
<td className="px-6 py-4 whitespace-nowrap">{user.name}</td>
<td className="px-6 py-4 whitespace-nowrap">{user.email}</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`px-2 py-1 text-xs rounded-full ${
user.isAdmin ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'
}`}>
{user.isAdmin ? 'Admin' : 'User'}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap">
{/* Add actions if needed */}
</td>
</tr>
))}
</tbody>
</table>
</>
)} )}
{activeTab === 'upload' && <DocumentUpload />} {activeTab === 'upload' && <DocumentUpload />}

View File

@ -1,122 +1,237 @@
import { useEffect } from "react"; // import { useEffect } from 'react';
import "./lab.css";
// export default function Lab() {
// useEffect(() => {
// window.scrollTo({ top: 0, behavior: "smooth" })
// }, [])
// return (
// <div className="container mx-auto px-4">
// {/* Header */}
// <div className="mt-24 mb-5 md:mb-32 text-center">
// <h1 className="text-2xl capitalize underline">
// во нашата лабораторија се вршат
// </h1>
// </div>
// {/* Main Content */}
// <div className="flex flex-wrap gap-12 justify-around">
// {/* Concrete Testing Section */}
// <div className="flex flex-col md:flex-row justify-between items-center gap-4 md:gap-72 mb-20 w-full">
// <div className="w-full md:w-1/2 px-8 py-6 text-center md:text-left bg-slate-100">
// <p className="mb-4">
// <strong className="text-xl block mb-2">
// Испитувањето на бетон
// </strong>
// е важен процес кој се користи за да се оцени квалитетот и издржливоста на бетонски конструкции.
// </p>
// <p>
// Испитувањето на бетонот е неопходен процес за осигурување на квалитет и безбедност на градежните проекти и инфраструктурата воопшто.
// Тоа помага во отстранивање на недостатоците и намалување на ризиците од евентуални пропаднувања на конструкциите.
// </p>
// </div>
// <div className="hidden md:block w-1/2">
// <img
// src="/kocki.webp"
// alt="Испитување на бетон"
// loading="lazy"
// className="max-w-full h-auto"
// />
// </div>
// </div>
// {/* Ore and Metal Testing Section */}
// <div className="flex flex-col md:flex-row justify-between items-center gap-4 md:gap-72 mb-20 w-full">
// <div className="hidden md:block w-1/2">
// <img
// src="/lab.webp"
// alt="Испитување на руди и метали"
// loading="lazy"
// className="max-w-full h-auto"
// />
// </div>
// <div className="w-full md:w-1/2 px-8 py-6 text-center md:text-left bg-slate-100">
// <p className="mb-4">
// <strong className="text-xl block mb-2">
// Испитувањето на руди, метали и троски
// </strong>
// е важен процес во областа на геолошкото и металуршко инженерство, со цел да се анализираат и одредат својствата и составот на суровините и производите.
// </p>
// <p>
// Овие испитувања се изведуваат за да се осигура квалитетот и спецификациите на материјалите, како и за да се обезбеди безбедно и ефикасно користење и обработка на руди, метали и троски во различни индустрии и апликации.
// </p>
// </div>
// </div>
// {/* Steel Testing Section */}
// <div className="flex flex-col md:flex-row justify-between items-center gap-4 md:gap-72 mb-20 w-full">
// <div className="w-full md:w-1/2 px-8 py-6 text-center md:text-left bg-slate-100">
// <p className="mb-4">
// <strong className="text-xl block mb-2">
// Испитувањето на челик
// </strong>
// е важен процес за одредување на квалитетот, издржливоста и други механички својства на челикот, кој се користи во разни индустрии и конструкции.
// </p>
// <p>
// Испитувањето на челик е неопходен процес за да се обезбеди квалитетот и безбедноста на производите и конструкциите кои го користат, како и за да се уверите дека челикот одговара на спецификациите и стандардите.
// </p>
// </div>
// <div className="hidden md:block w-1/2">
// <img
// src="/armatura.jpg"
// alt="Испитување на челик"
// loading="lazy"
// className="max-w-full h-auto"
// />
// </div>
// </div>
// {/* Laboratory Extraction Section */}
// <div className="flex flex-col md:flex-row justify-between items-center gap-4 md:gap-72 mb-20 w-full">
// <div className="hidden md:block w-1/2">
// <img
// src="/geotehnika.webp"
// alt="Лабораториска екстракција"
// loading="lazy"
// className="max-w-full h-auto"
// />
// </div>
// <div className="w-full md:w-1/2 px-8 py-6 text-center md:text-left bg-slate-100">
// <p className="mb-4">
// <strong className="text-xl block mb-2">
// Лабораториската екстракција
// </strong>
// на метали или неметали од руди и троски е процес кој се користи за да се изолираат и оддели материјалите од суровините или отпадните производи, како што се рудите или троските, со цел да се добие чист метал или неметал.
// </p>
// </div>
// </div>
// </div>
// </div>
// );
// }
import { useEffect } from 'react';
export default function Lab() { export default function Lab() {
useEffect(() => { useEffect(() => {
window.scrollTo({ top: 0, behavior: "smooth" }) window.scrollTo({ top: 0, behavior: "smooth" })
}, []) }, [])
return ( return (
<> <div className="min-h-screen bg-gray-50">
<div className="wrapper"> <div className="container mx-auto px-4 py-12">
<div className="heading sm:mb-5 md:mb-32"> {/* Header */}
<h1>во нашата лабораторија се вршат</h1> <div className="mt-8 mb-16 text-center">
<h1 className="text-3xl md:text-4xl font-semibold capitalize text-gray-800 mb-4">
во нашата лабораторија се вршат
</h1>
<div className="w-24 h-1 bg-blue-500 mx-auto"></div>
</div> </div>
<div className="text"> {/* Main Content */}
<div className="group md:bg-slate-300 sm:bg-none"> <div className="space-y-16">
<div className="textBox md:ml-10 bg-slate-100 sm:ml-0"> {/* Concrete Testing Section */}
<p> <div className="flex flex-col md:flex-row items-center gap-8 md:gap-16">
<strong>Испитувањето на бетон</strong> е важен процес кој се <div className="w-full md:w-1/2 p-8 bg-white rounded-xl shadow-lg hover:shadow-xl transition-shadow duration-300">
користи за да се оцени квалитетот и издржливоста на бетонски <h2 className="text-2xl font-bold text-gray-800 mb-4">
конструкции. Испитувањето на бетон
</p> </h2>
<p> <div className="space-y-4 text-gray-600">
Испитувањето на бетонот е неопходен процес за осигурување на <p>
квалитет и безбедност на градежните проекти и инфраструктурата е важен процес кој се користи за да се оцени квалитетот и издржливоста на бетонски конструкции.
воопшто. Тоа помага во отстранивање на недостатоците и </p>
намалување на ризиците од евентуални пропаднувања на <p>
конструкциите. Испитувањето на бетонот е неопходен процес за осигурување на квалитет и безбедност на градежните проекти и инфраструктурата воопшто.
</p> Тоа помага во отстранивање на недостатоците и намалување на ризиците од евентуални пропаднувања на конструкциите.
</p>
</div>
</div> </div>
<div className="imgBox md:mr-10 my-10 sm:mr-0"> <div className="w-full md:w-1/2">
<img src="/kocki.webp" alt="" loading="lazy" /> <img
</div> src="/kocki.webp"
</div> alt="Испитување на бетон"
<div className="group md:bg-slate-300 sm:bg-none">
<div className="imgBox md:ml-10 my-10 sm:ml-0">
<img
id="geotehnika"
src="/geotehnika.webp"
alt=""
loading="lazy" loading="lazy"
className="rounded-xl shadow-lg w-full h-[400px] object-cover hover:scale-105 transition-transform duration-300"
/> />
</div> </div>
<div className="textBox md:mr-10 bg-slate-100 sm:mr-0">
<p>
<strong>Геотехничките испитувања</strong> се однесуваат на
процесот на испитување и анализирање на геолошките и
геотехничките својства на земјиштето за да се одреди неговата
способност да ги поддржат инфраструктурните конструкции и
проекти.
</p>
<p>
Геотехничките испитувања помагаат во разбирањето на
геотехничките услови на градежното место и обезбедуваат
информации за дизајнирање и изградба на стабилни и издржливи
инфраструктурни објекти и конструкции.
</p>
</div>
</div> </div>
<div className="group md:bg-slate-300 sm:bg-none">
<div className="textBox md:ml-10 bg-slate-100 sm:ml-0"> {/* Ore and Metal Testing Section */}
<p> <div className="flex flex-col md:flex-row-reverse items-center gap-8 md:gap-16">
<strong>Испитувањето на руди, метали и троски </strong>е важен <div className="w-full md:w-1/2 p-8 bg-white rounded-xl shadow-lg hover:shadow-xl transition-shadow duration-300">
процес во областа на геолошкото и металуршко инженерство, со цел <h2 className="text-2xl font-bold text-gray-800 mb-4">
да се анализираат и одредат својствата и составот на суровините Испитувањето на руди, метали и троски
и производите. </h2>
</p> <div className="space-y-4 text-gray-600">
<p> <p>
Овие испитувања се изведуваат за да се осигура квалитетот и е важен процес во областа на геолошкото и металуршко инженерство, со цел да се анализираат и одредат својствата и составот на суровините и производите.
спецификациите на материјалите, како и за да се обезбеди </p>
безбедно и ефикасно користење и обработка на руди, метали и <p>
троски во различни индустрии и апликации. Овие испитувања се изведуваат за да се осигура квалитетот и спецификациите на материјалите, како и за да се обезбеди безбедно и ефикасно користење и обработка на руди, метали и троски во различни индустрии и апликации.
</p> </p>
</div>
</div> </div>
<div className="imgBox md:mr-10 my-10 sm:mr-0"> <div className="w-full md:w-1/2">
<img id="geotehnika" src="/lab.webp" alt="" loading="lazy" /> <img
src="/lab.webp"
alt="Испитување на руди и метали"
loading="lazy"
className="rounded-xl shadow-lg w-full h-[400px] object-cover hover:scale-105 transition-transform duration-300"
/>
</div> </div>
</div> </div>
<div className="group md:bg-slate-300 sm:bg-none"> {/* Steel Testing Section */}
<div className="imgBox md:ml-10 my-10 sm:ml-0"> <div className="flex flex-col md:flex-row items-center gap-8 md:gap-16">
<img id="geotehnika" src="/armatura.jpg" alt="" loading="lazy" /> <div className="w-full md:w-1/2 p-8 bg-white rounded-xl shadow-lg hover:shadow-xl transition-shadow duration-300">
<h2 className="text-2xl font-bold text-gray-800 mb-4">
Испитувањето на челик
</h2>
<div className="space-y-4 text-gray-600">
<p>
е важен процес за одредување на квалитетот, издржливоста и други механички својства на челикот, кој се користи во разни индустрии и конструкции.
</p>
<p>
Испитувањето на челик е неопходен процес за да се обезбеди квалитетот и безбедноста на производите и конструкциите кои го користат, како и за да се уверите дека челикот одговара на спецификациите и стандардите.
</p>
</div>
</div> </div>
<div className="w-full md:w-1/2">
<div className="textBox md:mr-10 bg-slate-100 sm:mr-0"> <img
<p> src="/armatura.jpg"
<strong>Испитувањето на челик </strong>е важен процес за alt="Испитување на челик"
одредување на квалитетот, издржливоста и други механички loading="lazy"
својства на челикот, кој се користи во разни индустрии и className="rounded-xl shadow-lg w-full h-[400px] object-cover hover:scale-105 transition-transform duration-300"
конструкции. />
</p>
<p>
Испитувањето на челик е неопходен процес за да се обезбеди
квалитетот и безбедноста на производите и конструкциите кои го
користат, како и за да се уверите дека челикот одговара на
спецификациите и стандардите.
</p>
</div> </div>
</div> </div>
<div className="group md:bg-slate-300 sm:bg-none"> {/* Laboratory Extraction Section */}
<div className="textBox md:ml-10 bg-slate-100 sm:ml-0"> <div className="flex flex-col md:flex-row-reverse items-center gap-8 md:gap-16">
<p> <div className="w-full md:w-1/2 p-8 bg-white rounded-xl shadow-lg hover:shadow-xl transition-shadow duration-300">
<strong>Лабораториската екстракција</strong> на метали или <h2 className="text-2xl font-bold text-gray-800 mb-4">
неметали од руди и троски е процес кој се користи за да се Лабораториската екстракција
изолираат и оддели материјалите од суровините или отпадните </h2>
производи, како што се рудите или троските, со цел да се добие <div className="space-y-4 text-gray-600">
чист метал или неметал. <p>
</p> на метали или неметали од руди и троски е процес кој се користи за да се изолираат и оддели материјалите од суровините или отпадните производи, како што се рудите или троските, со цел да се добие чист метал или неметал.
</p>
</div>
</div> </div>
<div className="imgBox md:mr-10 my-10 sm:mr-0"> <div className="w-full md:w-1/2">
<img id="geotehnika" src="/ekstrakcija.jpg" alt="" loading="lazy" /> <img
src="/geotehnika.webp"
alt="Лабораториска екстракција"
loading="lazy"
className="rounded-xl shadow-lg w-full h-[400px] object-cover hover:scale-105 transition-transform duration-300"
/>
</div> </div>
</div> </div>
</div> </div>
</div > </div>
</> </div>
); );
} }

View File

@ -2,7 +2,7 @@ import { useState } from "react";
import { Dialog } from "@headlessui/react"; import { Dialog } from "@headlessui/react";
import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline"; import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline";
import { NavLink } from "react-router-dom"; import { NavLink } from "react-router-dom";
import "./navbar.css";
const navigation = [ const navigation = [
{ name: "Дома", href: "/" }, { name: "Дома", href: "/" },
{ name: "За нас", href: "/about" }, { name: "За нас", href: "/about" },
@ -17,107 +17,117 @@ export default function Navbar() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
return ( return (
<> <div className="absolute w-full z-50">
<div className=""> <header className="relative bg-transparent">
<div className="bg-white"> <nav
<header className="nav"> className="container mx-auto flex items-center justify-between p-6 lg:px-8"
<nav aria-label="Global"
className="flex items-center justify-between p-6 lg:px-8" >
aria-label="Global" {/* Logo */}
<div className="flex lg:flex-1">
<a href="/" className="flex items-center -m-1.5 p-1.5">
<img
className="h-12 w-auto hover:scale-105 transition-transform duration-300"
src="/imklogorgb.png"
alt="IMK logo"
/>
</a>
</div>
{/* Mobile menu button */}
<div className="flex lg:hidden">
<button
type="button"
className="-m-2.5 inline-flex items-center justify-center rounded-md p-2.5 text-white hover:bg-white/10 transition-colors duration-300"
onClick={() => setMobileMenuOpen(true)}
> >
<div id="logo" className=""> <span className="sr-only">Open main menu</span>
<div className="flex lg:flex-1"> <Bars3Icon className="h-6 w-6" aria-hidden="true" />
<a href="#" className="-m-1.5 p-1.5"> </button>
<span></span> </div>
<img
className="h-14 w-auto" {/* Desktop navigation */}
src="/imklogorgb.png" <div className="hidden lg:flex lg:gap-x-12">
alt="logo" {navigation.map((item) => (
/> <NavLink
</a> key={item.name}
to={item.href}
className={({ isActive }) =>
`text-base font-semibold leading-6 transition-all duration-300
${isActive
? 'text-white border-b-2 border-white'
: 'text-gray-200 hover:text-white hover:border-b-2 hover:border-white/50'
}`
}
>
{item.name}
</NavLink>
))}
</div>
{/* TUF Logo */}
<div className="hidden lg:flex lg:flex-1 lg:justify-end">
<a href="/" className="flex items-center -m-1.5 p-1.5">
<img
className="h-12 w-auto hover:scale-105 transition-transform duration-300"
src="/tuf.png"
alt="TUF logo"
/>
</a>
</div>
</nav>
{/* Mobile menu */}
<Dialog
as="div"
className="lg:hidden"
open={mobileMenuOpen}
onClose={setMobileMenuOpen}
>
<div className="fixed inset-0 z-50" />
<Dialog.Panel className="fixed inset-y-0 right-0 z-50 w-full overflow-y-auto bg-gradient-to-b from-blue-900 to-blue-800 px-6 py-6 sm:max-w-sm">
<div className="flex items-center justify-between">
<a href="/" className="-m-1.5 p-1.5">
<img
className="h-8 w-auto"
src="/imklogorgb.png"
alt="IMK logo"
/>
</a>
<button
type="button"
className="-m-2.5 rounded-md p-2.5 text-white hover:bg-white/10 transition-colors duration-300"
onClick={() => setMobileMenuOpen(false)}
>
<span className="sr-only">Close menu</span>
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
<div className="mt-6 flow-root">
<div className="-my-6 divide-y divide-white/10">
<div className="space-y-2 py-6">
{navigation.map((item) => (
<NavLink
key={item.name}
to={item.href}
className={({ isActive }) =>
`-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 transition-colors duration-300
${isActive
? 'text-white bg-white/10'
: 'text-gray-200 hover:bg-white/5 hover:text-white'
}`
}
onClick={() => setMobileMenuOpen(false)}
>
{item.name}
</NavLink>
))}
</div> </div>
</div> </div>
<div className="flex lg:hidden"> </div>
<button </Dialog.Panel>
type="button" </Dialog>
className="-m-2.5 inline-flex items-center justify-center rounded-md p-2.5 text-gray-700" </header>
onClick={() => setMobileMenuOpen(true)} </div>
>
<span className="sr-only">Open main menu</span>
<Bars3Icon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
<div className="hidden lg:flex lg:gap-x-12 navitems">
{navigation.map((item) => (
<NavLink
key={item.name}
to={item.href}
className="text-lg font-semibold leading-6 text-gray-500"
>
{item.name}
</NavLink>
))}
</div>
<div id="logo-tuf" className="">
<div className="flex lg:flex-1">
<a href="#" className="-m-1.5 p-1.5">
<span></span>
<img
className="h-14 w-auto"
src="/tuf.png"
alt="tuf logo"
/>
</a>
</div>
</div>
</nav>
<Dialog
as="div"
className="lg:hidden"
open={mobileMenuOpen}
onClose={setMobileMenuOpen}
>
<div className="fixed inset-0 z-50" />
<Dialog.Panel className="fixed inset-y-0 right-0 z-50 w-full overflow-y-auto bg-white px-6 py-6 sm:max-w-sm sm:ring-1 sm:ring-gray-900/10">
<div className="flex items-center justify-between">
<NavLink href="#" className="-m-1.5 p-1.5">
<span className="sr-only">ИМК</span>
{/* <img
className="h-8 w-auto"
src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600"
alt=""
/> */}
</NavLink>
<button
type="button"
className="-m-2.5 rounded-md p-2.5 text-gray-700"
onClick={() => setMobileMenuOpen(false)}
>
<span className="sr-only">Close menu</span>
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
<div className="mt-6 flow-root">
<div className="-my-6 divide-y divide-gray-500/10">
<div className="space-y-2 py-6">
{navigation.map((item) => (
<NavLink
key={item.name}
to={item.href}
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50"
onClick={() => setMobileMenuOpen(false)}
>
{item.name}
</NavLink>
))}
</div>
</div>
</div>
</Dialog.Panel>
</Dialog>
</header>
</div>
</div>
</>
); );
} }

View File

@ -1,124 +1,195 @@
import './about.css' // import './about.css'
// export default function About() {
// return (
// <>
// <div className="aboutGroup">
// {/* <div className="container"> */}
// <div className="aboutContainer">
// <h1 className="text-2xl text-center mt-32 font-bold tracking-tight text-gray-900 sm:text-4xl">За нас</h1>
// <p className="tracking-tight text-gray-900 sm:text-4xl mt-16">Компанијата Испитување на материјали и консултантство ДООЕЛ е основана во 2021 година. Главната дејност на ИМК ДООЕЛ е лабораториски испитувања во
// областа на градежништвото и индустријата, како и консултантски услуги во областа на индустријата. Со успешното работење во своите области и
// стручниот кадар кој компанијата го поседува, во 2022 година за компанијата интерес пројавува потенцијален партнер од Швајцарија. Во 2023
// година компанијата ИМК ДООЕЛ од домашна компанија прераснува во интернационална компанија со добивање на партнер од Швајцарија и
// прераснува во ИМК ДОО. Во истата година компанијата добива финансиска поддршка од старт ап фондот СЕКО, фонд кој го препозна квалитетот и
// визијата кој го поседуваат двајца млади претпиемачи. Лабораторијата е комплетно опремена да одговори на секој предизвик во областа на
// испитувањето на квалитетот на материјалите во градежната индсутрија со најмодерни инструменти за теренско и лабораториско испитување.
// Испитувањето на материјалите се врши според најнови EN и МКС стандарди.</p>
// </div>
// <div className="form-container">
// <div className="mx-auto max-w-2xl text-center">
// <h2 className="text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">Станете дел од нашиот тим</h2>
// </div>
// <form action="https://formsubmit.co/taratur@gmail.com"
// enctype='multipart/form-data'
// method="POST" 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" className="block text-sm font-semibold leading-6 text-gray-900">
// Име
// </label>
// <div className="mt-2.5">
// <input
// type="text"
// name="first-name"
// id="first-name"
// autoComplete="given-name"
// className="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
// />
// </div>
// </div>
// <div>
// <label htmlFor="last-name" className="block text-sm font-semibold leading-6 text-gray-900">
// Презиме
// </label>
// <div className="mt-2.5">
// <input
// type="text"
// name="last-name"
// id="last-name"
// autoComplete="family-name"
// className="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
// />
// </div>
// </div>
// <div className="sm:col-span-2">
// <label htmlFor="email" className="block text-sm font-semibold leading-6 text-gray-900">
// Мејл
// </label>
// <div className="mt-2.5">
// <input
// type="email"
// name="email"
// id="email"
// autoComplete="email"
// className="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
// />
// </div>
// </div>
// <div className="sm:col-span-2">
// <label htmlFor="message" className="block text-sm font-semibold leading-6 text-gray-900">
// Порака
// </label>
// <div className="mt-2.5">
// <textarea
// name="message"
// id="message"
// rows={4}
// className="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
// defaultValue={''}
// />
// </div>
// </div>
// <div className="sm:col-span-2">
// <label htmlFor="attachment" className="block text-lg font-semibold leading-6 text-gray-900 mb-4">
// Прокачетего вашето CV (5mb max)
// </label>
// <input type="file" name="attachment" id='attachment' accept=".pdf, image/jpeg, .doc, .odt" />
// </div>
// {/* <Switch.Group as="div" className="flex gap-x-4 sm:col-span-2">
// </Switch.Group> */}
// </div>
// <input type="hidden" name="_next" value="https://imk-azure.vercel.app/"></input>
// <div className="mt-10">
// <button
// type="submit"
// className="block w-full rounded-md bg-gray-500 px-3.5 py-2.5 text-center text-sm font-semibold text-white shadow-sm hover:bg-gray-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-500"
// >
// Испрати
// </button>
// </div>
// </form>
// {/* </div > */}
// </div>
// </div>
// </>
// )
// }
import { SectionHeader } from '../../shared/SectionHeader';
import { Button } from '../../shared/Button';
export default function About() { export default function About() {
return (
<div className="min-h-screen bg-gradient-to-b from-gray-50 to-white">
{/* Hero Section */}
<div className="relative h-[60vh] overflow-hidden">
<div className="absolute inset-0">
<img
src="/about-hero.jpg"
alt="About Us"
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-r from-blue-900/90 to-blue-600/80" />
</div>
<div className="relative container mx-auto px-4 h-full flex items-center">
<div className="max-w-3xl">
<h1 className="text-4xl md:text-6xl font-bold text-white mb-6">
За Нас
</h1>
<p className="text-xl text-gray-200 mb-8">
Повеќе од 15 години искуство во обезбедување квалитет
</p>
</div>
</div>
</div>
{/* Content Sections */}
<div className="container mx-auto px-4 py-16">
<SectionHeader
title="Нашата Мисија"
subtitle="Посветени сме на обезбедување највисок квалитет во нашата работа"
/>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<div className="bg-white rounded-xl shadow-lg p-8">
<h3 className="text-2xl font-semibold text-gray-800 mb-4">
Визија
</h3>
<p className="text-gray-600">
Да бидеме водечка компанија во обезбедување на квалитет и сигурност во градежништвото.
</p>
</div>
<div className="bg-white rounded-xl shadow-lg p-8">
<h3 className="text-2xl font-semibold text-gray-800 mb-4">
Вредности
</h3>
<p className="text-gray-600">
Професионалност, посветеност и иновација во секој проект.
</p>
</div>
</div>
</div>
return ( {/* CTA Section */}
<> <div className="bg-blue-600 text-white py-24">
<div className="aboutGroup"> <div className="container mx-auto px-4 text-center">
<h2 className="text-3xl md:text-4xl font-bold mb-8">
Започнете го вашиот проект со нас
{/* <div className="container"> */} </h2>
<div className="aboutContainer"> <Button variant="secondary">
<h1 className="text-2xl text-center mt-32 font-bold tracking-tight text-gray-900 sm:text-4xl">За нас</h1> Контактирајте нѐ
<p className="tracking-tight text-gray-900 sm:text-4xl mt-16">Компанијата Испитување на материјали и консултантство ДООЕЛ е основана во 2021 година. Главната дејност на ИМК ДООЕЛ е лабораториски испитувања во </Button>
областа на градежништвото и индустријата, како и консултантски услуги во областа на индустријата. Со успешното работење во своите области и </div>
стручниот кадар кој компанијата го поседува, во 2022 година за компанијата интерес пројавува потенцијален партнер од Швајцарија. Во 2023 </div>
година компанијата ИМК ДООЕЛ од домашна компанија прераснува во интернационална компанија со добивање на партнер од Швајцарија и </div>
прераснува во ИМК ДОО. Во истата година компанијата добива финансиска поддршка од старт ап фондот СЕКО, фонд кој го препозна квалитетот и );
визијата кој го поседуваат двајца млади претпиемачи. Лабораторијата е комплетно опремена да одговори на секој предизвик во областа на
испитувањето на квалитетот на материјалите во градежната индсутрија со најмодерни инструменти за теренско и лабораториско испитување.
Испитувањето на материјалите се врши според најнови EN и МКС стандарди.</p>
</div>
<div className="form-container">
<div className="mx-auto max-w-2xl text-center">
<h2 className="text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">Станете дел од нашиот тим</h2>
</div>
<form action="https://formsubmit.co/taratur@gmail.com"
enctype='multipart/form-data'
method="POST" 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" className="block text-sm font-semibold leading-6 text-gray-900">
Име
</label>
<div className="mt-2.5">
<input
type="text"
name="first-name"
id="first-name"
autoComplete="given-name"
className="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div>
<label htmlFor="last-name" className="block text-sm font-semibold leading-6 text-gray-900">
Презиме
</label>
<div className="mt-2.5">
<input
type="text"
name="last-name"
id="last-name"
autoComplete="family-name"
className="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div className="sm:col-span-2">
<label htmlFor="email" className="block text-sm font-semibold leading-6 text-gray-900">
Мејл
</label>
<div className="mt-2.5">
<input
type="email"
name="email"
id="email"
autoComplete="email"
className="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div className="sm:col-span-2">
<label htmlFor="message" className="block text-sm font-semibold leading-6 text-gray-900">
Порака
</label>
<div className="mt-2.5">
<textarea
name="message"
id="message"
rows={4}
className="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
defaultValue={''}
/>
</div>
</div>
<div className="sm:col-span-2">
<label htmlFor="attachment" className="block text-lg font-semibold leading-6 text-gray-900 mb-4">
Прокачетего вашето CV (5mb max)
</label>
<input type="file" name="attachment" id='attachment' accept=".pdf, image/jpeg, .doc, .odt" />
</div>
{/* <Switch.Group as="div" className="flex gap-x-4 sm:col-span-2">
</Switch.Group> */}
</div>
<input type="hidden" name="_next" value="https://imk-azure.vercel.app/"></input>
<div className="mt-10">
<button
type="submit"
className="block w-full rounded-md bg-gray-500 px-3.5 py-2.5 text-center text-sm font-semibold text-white shadow-sm hover:bg-gray-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-500"
>
Испрати
</button>
</div>
</form>
{/* </div > */}
</div>
</div>
</>
)
} }

View File

@ -1,235 +1,190 @@
import { NavLink } from "react-router-dom"; import { NavLink } from "react-router-dom";
import "./home.css"; import { motion } from "framer-motion"; // You'll need to install framer-motion
import { ArrowRightIcon, CheckCircleIcon } from "@heroicons/react/24/outline";
const Home = () => { const serviceCards = [
{
title: "Лабораториски услуги",
description: "Професионални лабораториски испитувања со најсовремена опрема",
image: "/labfinal.webp",
link: "/lab",
services: [
"Испитување на бетон",
"Геотехнички испитувања",
"Испитување на челик",
"Испитување на руди, метали и троски",
"Лабораториска екстракција од руди и троски"
]
},
// ... other service cards
];
export default function Home() {
return ( return (
<> <div className="min-h-screen bg-gradient-to-b from-gray-50 to-white">
<div className="homeContainer"> {/* Hero Section with Parallax */}
<div className="bannerTop"> <div className="relative h-[90vh] overflow-hidden">
<div className="slogan md:bg-slate-300 w-full py-10 sm:bg-none"> <div className="absolute inset-0">
<h1 className=""> <img
ИМК: Вашиот партнер во контролата на безбедно градење и квалитетно src="/hero-background.jpg"
живеење alt="Laboratory Background"
</h1> className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-r from-blue-900/90 to-blue-600/80" />
</div>
<div className="relative container mx-auto px-4 h-full flex items-center">
<div className="max-w-3xl">
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="text-4xl md:text-6xl font-bold text-white mb-6"
>
Вашиот партнер во контролата на безбедно градење
</motion.h1>
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="text-xl text-gray-200 mb-8"
>
Професионални лабораториски услуги со најсовремена опрема и експертиза
</motion.p>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.4 }}
className="flex gap-4"
>
<NavLink
to="/contact"
className="bg-blue-500 hover:bg-blue-600 text-white px-8 py-3 rounded-full font-semibold transition-colors duration-300"
>
Контактирајте нѐ
</NavLink>
<NavLink
to="/services"
className="bg-white/10 hover:bg-white/20 text-white px-8 py-3 rounded-full font-semibold transition-colors duration-300"
>
Дознајте повеќе
</NavLink>
</motion.div>
</div> </div>
</div> </div>
</div>
<div className="imgHelper"> {/* Stats Section */}
<div className=""> <div className="container mx-auto px-4 py-16">
<div className="grid grid-cols-1 grid-rows-1 md:grid-cols-3 gap-16 mt-16"> <div className="grid grid-cols-1 md:grid-cols-4 gap-8">
<div className="block rounded-lg bg-white shadow-[0_2px_15px_-3px_rgba(0,0,0,0.07),0_10px_20px_-2px_rgba(0,0,0,0.04)] dark:bg-neutral-700"> {[
<NavLink to="/lab"> { number: "15+", label: "Години искуство" },
<div { number: "1000+", label: "Задоволни клиенти" },
className="relative overflow-hidden bg-cover bg-no-repeat" { number: "5000+", label: "Завршени проекти" },
data-te-ripple-init { number: "100%", label: "Задоволство" },
data-te-ripple-color="light" ].map((stat) => (
> <div key={stat.label} className="text-center">
<img <div className="text-4xl font-bold text-blue-600 mb-2">{stat.number}</div>
style={{ <div className="text-gray-600">{stat.label}</div>
width: '100%', </div>
height: '100%', ))}
objectFit: 'cover', </div>
objectPosition: 'center' </div>
}}
className="rounded-t-lg"
src="/labfinal.webp"
alt=""
loading="lazy"
/>
<a href="#!">
<div className="absolute bottom-0 left-0 right-0 top-0 h-full w-full overflow-hidden bg-[hsla(0,0%,98%,0.15)] bg-fixed opacity-0 transition duration-300 ease-in-out hover:opacity-100"></div>
</a>
</div>
<div className="p-6">
<h5 className="mb-2 text-xl text-center font-medium leading-tight text-neutral-800 dark:text-neutral-50">
Лабораториски услуги
</h5>
{/* <p className="mb-4 text-base text-neutral-600 dark:text-neutral-200">
Some quick example text to build on the card title and
make up the bulk of the card's content.
</p> */}
{/* <p className="mb-4 text-base text-neutral-600 dark:text-neutral-200">Нашата лабораторија ви нуди...</p> */}
{/* <button
type="button"
className="inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-[0_4px_9px_-4px_#3b71ca] transition duration-150 ease-in-out hover:bg-primary-600 hover:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.3),0_4px_18px_0_rgba(59,113,202,0.2)] focus:bg-primary-600 focus:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.3),0_4px_18px_0_rgba(59,113,202,0.2)] focus:outline-none focus:ring-0 active:bg-primary-700 active:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.3),0_4px_18px_0_rgba(59,113,202,0.2)] dark:shadow-[0_4px_9px_-4px_rgba(59,113,202,0.5)] dark:hover:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.2),0_4px_18px_0_rgba(59,113,202,0.1)] dark:focus:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.2),0_4px_18px_0_rgba(59,113,202,0.1)] dark:active:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.2),0_4px_18px_0_rgba(59,113,202,0.1)]"
data-te-ripple-init
data-te-ripple-color="light"
>
Прочитај повеќе
</button> */}
</div>
</NavLink>
</div>
<NavLink to="/ultrasound">
<div className="block rounded-lg bg-white shadow-[0_2px_15px_-3px_rgba(0,0,0,0.07),0_10px_20px_-2px_rgba(0,0,0,0.04)] dark:bg-neutral-700">
<div
className="relative overflow-hidden bg-cover bg-no-repeat"
data-te-ripple-init
data-te-ripple-color="light"
>
<img
style={{
width: '100%',
height: '100%',
objectFit: 'cover',
objectPosition: 'center'
}}
className="rounded-t-lg"
src="wallscener.jpeg"
alt=""
loading="lazy"
/>
<a href="#!">
<div className="absolute bottom-0 left-0 right-0 top-0 h-full w-full overflow-hidden bg-[hsla(0,0%,98%,0.15)] bg-fixed opacity-0 transition duration-300 ease-in-out hover:opacity-100"></div>
</a>
</div>
<div className="p-6">
<h5 className="mb-2 text-xl text-center font-medium leading-tight text-neutral-800 dark:text-neutral-50">
Испитување со ултразвук
</h5>
{/* <p className="mb-4 text-base text-neutral-600 dark:text-neutral-200">Најсовремена радарска технологија.</p> */}
{/* <p className="mb-4 text-base text-neutral-600 dark:text-neutral-200"> {/* Services Section */}
Some quick example text to build on the card title and make <div className="bg-gray-100 py-24">
up the bulk of the card's content. <div className="container mx-auto px-4">
</p> */} <div className="text-center mb-16">
{/* <button <h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-4">
type="button" Нашите Услуги
className="inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-[0_4px_9px_-4px_#3b71ca] transition duration-150 ease-in-out hover:bg-primary-600 hover:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.3),0_4px_18px_0_rgba(59,113,202,0.2)] focus:bg-primary-600 focus:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.3),0_4px_18px_0_rgba(59,113,202,0.2)] focus:outline-none focus:ring-0 active:bg-primary-700 active:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.3),0_4px_18px_0_rgba(59,113,202,0.2)] dark:shadow-[0_4px_9px_-4px_rgba(59,113,202,0.5)] dark:hover:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.2),0_4px_18px_0_rgba(59,113,202,0.1)] dark:focus:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.2),0_4px_18px_0_rgba(59,113,202,0.1)] dark:active:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.2),0_4px_18px_0_rgba(59,113,202,0.1)]" </h2>
data-te-ripple-init <div className="w-24 h-1 bg-blue-500 mx-auto"></div>
data-te-ripple-color="light" </div>
>
Прочитај повеќе <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
</button> */} {serviceCards.map((card) => (
<NavLink
key={card.title}
to={card.link}
className="group bg-white rounded-2xl shadow-lg overflow-hidden hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1"
>
<div className="relative h-56 overflow-hidden">
<img
src={card.image}
alt={card.title}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent" />
<h3 className="absolute bottom-4 left-4 text-xl font-semibold text-white">
{card.title}
</h3>
</div>
<div className="p-6">
<p className="text-gray-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-gray-700"
>
<CheckCircleIcon className="h-5 w-5 text-blue-500 mr-2" />
{service}
</li>
))}
</ul>
<div className="mt-4 flex items-center text-blue-500 font-semibold">
Дознајте повеќе
<ArrowRightIcon className="h-5 w-5 ml-2 group-hover:translate-x-2 transition-transform duration-300" />
</div> </div>
</div> </div>
</NavLink> </NavLink>
))}
</div>
</div>
</div>
{/* Featured Projects/Clients */}
<div className="block rounded-lg bg-white shadow-[0_2px_15px_-3px_rgba(0,0,0,0.07),0_10px_20px_-2px_rgba(0,0,0,0.04)] dark:bg-neutral-700"> <div className="container mx-auto px-4 py-24">
<div className="text-center mb-16">
<NavLink to="/consulting"> <h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-4">
<div Наши Клиенти
className="relative overflow-hidden bg-cover bg-no-repeat" </h2>
data-te-ripple-init <div className="w-24 h-1 bg-blue-500 mx-auto mb-8"></div>
data-te-ripple-color="light" <p className="text-gray-600 max-w-2xl mx-auto">
> Горди сме на довербата која ни ја укажуваат нашите клиенти
<img </p>
style={{ </div>
width: '100%',
height: '100%', <div className="grid grid-cols-2 md:grid-cols-4 gap-8">
objectFit: 'cover', {/* Add client logos here */}
objectPosition: 'center' {[1, 2, 3, 4].map((index) => (
}} <div
className="rounded-t-lg" key={index}
src="/consulting2.jpg" className="bg-white p-8 rounded-xl shadow-md hover:shadow-lg transition-shadow duration-300"
alt="" >
loading="lazy" <img
/> src={`/client-${index}.png`}
<a href="#!"> alt={`Client ${index}`}
<div className="absolute bottom-0 left-0 right-0 top-0 h-full w-full overflow-hidden bg-[hsla(0,0%,98%,0.15)] bg-fixed opacity-0 transition duration-300 ease-in-out hover:opacity-100"></div> className="w-full h-20 object-contain grayscale hover:grayscale-0 transition-all duration-300"
</a> />
</div>
<div className="p-6">
<h5 className="mb-2 text-xl text-center font-medium leading-tight text-neutral-800 dark:text-neutral-50">
Консултантски услуги
</h5>
{/* <p className="mb-4 text-base text-neutral-600 dark:text-neutral-200">Проектна документација, елаборати</p> */}
{/* <p className="mb-4 text-base text-neutral-600 dark:text-neutral-200">
Some quick example text to build on the card title and make
up the bulk of the card's content.
</p> */}
{/* <NavLink to={"/consulting"}> */}
{/* <button
type="button"
className="inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-[0_4px_9px_-4px_#3b71ca] transition duration-150 ease-in-out hover:bg-primary-600 hover:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.3),0_4px_18px_0_rgba(59,113,202,0.2)] focus:bg-primary-600 focus:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.3),0_4px_18px_0_rgba(59,113,202,0.2)] focus:outline-none focus:ring-0 active:bg-primary-700 active:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.3),0_4px_18px_0_rgba(59,113,202,0.2)] dark:shadow-[0_4px_9px_-4px_rgba(59,113,202,0.5)] dark:hover:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.2),0_4px_18px_0_rgba(59,113,202,0.1)] dark:focus:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.2),0_4px_18px_0_rgba(59,113,202,0.1)] dark:active:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.2),0_4px_18px_0_rgba(59,113,202,0.1)]"
data-te-ripple-init
data-te-ripple-color="light"
>
Прочитај повеќе
</button> */}
</div>
</NavLink>
</div>
</div> </div>
</div> ))}
</div> </div>
<div className="bannerBottom mt-16"> </div>
<div className="md:bg-slate-300 w-full py-1 my-32 sm:bg-none">
<h2>
Осигурајте го Вашиот бизнис план со нас и дозволете ни да ви
помогнеме во Вашиот успех
</h2>
</div>
</div>
<div className="someclass">
<div className="team">
{/* <h1>Станете дел од нашиот тим</h1> */}
<img src="/naum2.webp" alt="" />
</div>
</div>
{/* slide show start */}
{/* slide show end */}
<div className="listCards">
<div className="serviceCard">
<NavLink to="/lab">
<div>
<h1>Лабораторија</h1>
</div>
<ul>
<li>Испитување на бетон</li>
<li>Геотехнички испитувања</li>
<li>Испитување на челик</li>
<li>Испитување на руди, метали и троски</li>
<li>Лабораториска екстракција од руди и троски</li>
</ul>
</NavLink>
</div>
<div className="serviceCard">
<NavLink to="/ultrasound">
<div>
<h1>Испитување со ултразвук</h1>
</div>
<ul>
<li>
Испитување на број и распоред на арматурни шипки
</li>
<li>Детектирање на електрична инсталација</li>
<li>Детектирање на водоводна инсталација и пукнати цевки во ѕид</li>
</ul>
</NavLink>
</div>
<div className="serviceCard">
<NavLink to="/consulting">
<div>
<h1>Консултанстски услуги</h1>
</div>
<ul>
<li>Изработка на Б интегрирани дозволи</li>
<li>Изработка на А интегрирани дозволи</li>
<li>Изработка на проценка на влијанија на животна средина</li>
<li>
Изработка на проектна документација за производствени погони
</li>
<li>
Изработка на cost efficiency проекти за производствени погони
</li>
<li>
Изработка на елаборати за финансиска исплатливост на
производствени процеси
</li>
</ul>
</NavLink>
</div>
{/* CTA Section */}
<div className="bg-blue-600 text-white py-24">
<div className="container mx-auto px-4 text-center">
<h2 className="text-3xl md:text-4xl font-bold mb-8">
Спремни сме да ви помогнеме во вашиот следен проект
</h2>
<NavLink
to="/contact"
className="inline-block bg-white text-blue-600 px-8 py-3 rounded-full font-semibold hover:bg-gray-100 transition-colors duration-300"
>
Контактирајте нѐ
</NavLink>
</div> </div>
</div > </div>
</> </div>
); );
}; }
export default Home;

View File

@ -14,7 +14,15 @@ api.interceptors.request.use((config) => {
} }
return config; return config;
}); });
export const createUser = (userData) => api.post('/admin/users', userData); // export const createUser = (userData) => api.post('/admin/users', userData);
export const createUser = (userData) => {
return api.post('/admin/users', {
name: userData.name,
email: userData.email,
password: userData.password,
isAdmin: userData.isAdmin // Add this line
});
};
export const login = (username, password) => api.post('/auth/login', { username, password }); export const login = (username, password) => api.post('/auth/login', { username, password });
export const shareDocument = (documentId, userIds) => api.post(`/admin/documents/${documentId}/share`, { userIds }); export const shareDocument = (documentId, userIds) => api.post(`/admin/documents/${documentId}/share`, { userIds });
export const updateDocumentStatus = (documentId, status) => api.put(`/admin/documents/${documentId}/status`, { status }); export const updateDocumentStatus = (documentId, status) => api.put(`/admin/documents/${documentId}/status`, { status });

View File

@ -0,0 +1,18 @@
// Reusable button component
export function Button({ variant = 'primary', children, ...props }) {
const baseStyles = "px-8 py-3 rounded-full font-semibold transition-colors duration-300";
const variants = {
primary: "bg-blue-600 hover:bg-blue-700 text-white",
secondary: "bg-white/10 hover:bg-white/20 text-white",
outline: "border-2 border-blue-600 text-blue-600 hover:bg-blue-50"
};
return (
<button
className={`${baseStyles} ${variants[variant]}`}
{...props}
>
{children}
</button>
);
}

View File

@ -0,0 +1,17 @@
// Reusable section header component
// eslint-disable-next-line react/prop-types
export function SectionHeader({ title, subtitle }) {
return (
<div className="text-center mb-16">
<h2 className="text-3xl md:text-4xl font-bold text-gray-800 mb-4">
{title}
</h2>
<div className="w-24 h-1 bg-blue-600 mx-auto mb-8"></div>
{subtitle && (
<p className="text-gray-600 max-w-2xl mx-auto">
{subtitle}
</p>
)}
</div>
);
}

View File

@ -0,0 +1,20 @@
export const colors = {
primary: {
light: '#3B82F6', // blue-500
DEFAULT: '#2563EB', // blue-600
dark: '#1D4ED8', // blue-700
darker: '#1E40AF' // blue-800
},
background: {
light: '#F9FAFB', // gray-50
DEFAULT: '#F3F4F6', // gray-100
dark: '#E5E7EB' // gray-200
},
text: {
primary: '#1F2937', // gray-800
secondary: '#4B5563', // gray-600
light: '#9CA3AF' // gray-400
},
white: '#FFFFFF',
black: '#000000'
}

View File

@ -0,0 +1,25 @@
export const commonStyles = {
// Container
container: "container mx-auto px-4",
// Section Headers
sectionHeader: "text-center mb-16",
sectionTitle: "text-3xl md:text-4xl font-bold text-gray-800 mb-4",
sectionDivider: "w-24 h-1 bg-blue-600 mx-auto",
// Cards
card: "bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300",
// Buttons
primaryButton: "bg-blue-600 hover:bg-blue-700 text-white px-8 py-3 rounded-full font-semibold transition-colors duration-300",
secondaryButton: "bg-white/10 hover:bg-white/20 text-white px-8 py-3 rounded-full font-semibold transition-colors duration-300",
// Gradients
primaryGradient: "bg-gradient-to-r from-blue-900/90 to-blue-600/80",
// Text
heading1: "text-4xl md:text-6xl font-bold",
heading2: "text-3xl md:text-4xl font-bold",
heading3: "text-2xl font-semibold",
paragraph: "text-gray-600"
}