init
No files matched your search
@@ -0,0 +1,20 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react/jsx-runtime',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
||||
settings: { react: { version: '18.2' } },
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# React + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
# imk
|
||||
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<!-- <link rel="icon" type="image/svg+xml" href="/logo.svg" /> -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>IMK</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "imk",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.17",
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"axios": "^1.7.7",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.15",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@vitejs/plugin-react": "^4.0.3",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"eslint": "^8.45.0",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.3",
|
||||
"postcss": "^8.4.28",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"vite": "^4.4.5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 177 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 1003 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 774 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 2.1 MiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 416 KiB |
|
After Width: | Height: | Size: 310 KiB |
|
After Width: | Height: | Size: 2.6 MiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,56 @@
|
||||
#root {
|
||||
/* max-width: 1280px; */
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
/* text-align: center; */
|
||||
/* background-color: papayawhip; */
|
||||
background-color: rgb(228, 237, 240);
|
||||
/* background-image: url('../public/plocki.jpg'); */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@keyframes logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
a:nth-of-type(2) .logo {
|
||||
animation: logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/* .logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
|
||||
.logo.react:hover {
|
||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
||||
}
|
||||
*/
|
||||
body {
|
||||
background-color: rgb(255, 253, 253);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import './App.css'
|
||||
import Navbar from './components/navbar/Navbar'
|
||||
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
||||
import Home from './pages/homepage/Home'
|
||||
import About from './pages/aboutpage/About'
|
||||
import Contact from './pages/contactpage/Contact.jsx'
|
||||
import Consulting from './components/consalting/consulting';
|
||||
import Lab from './components/lab/lab';
|
||||
import Footer from './components/footer/footer';
|
||||
import UltraSound from './components/ultrasound/ultraSound';
|
||||
import Gallery from './components/gallery/Gallery.jsx';
|
||||
import Certificates from './components/Certificates/Certificates.jsx';
|
||||
import Clients from './components/clients/clients';
|
||||
import AdminPanel from './components/adminPanel/AdminPanel';
|
||||
import Login from './components/login/login';
|
||||
import Dashboard from './components/dashboard/Dashboard';
|
||||
import ProtectedRoute from './components/protectedRoute/ProtectedRoute';
|
||||
|
||||
function App() {
|
||||
|
||||
return (
|
||||
<div >
|
||||
<BrowserRouter >
|
||||
<Navbar />
|
||||
<Routes>
|
||||
<Route path='/' element={<Home />} />
|
||||
<Route path='/contact' element={<Contact />} />
|
||||
<Route path='/about' element={<About />} />
|
||||
<Route path='/consulting' element={<Consulting />} />
|
||||
<Route path='/lab' element={<Lab />} />
|
||||
<Route path='/ultrasound' element={<UltraSound />} />
|
||||
<Route path='/gallery' element={<Gallery />} />
|
||||
<Route path='/certificates' element={<Certificates />} />
|
||||
<Route path='/clients' element={<Clients />} />
|
||||
<Route path='/admin' element={
|
||||
<ProtectedRoute adminOnly={true}>
|
||||
<AdminPanel />
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
<Route path='/login' element={<Login />} />
|
||||
<Route path='/dashboard' element={
|
||||
<ProtectedRoute>
|
||||
<Dashboard />
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
</Routes>
|
||||
<Footer />
|
||||
</BrowserRouter>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
|
After Width: | Height: | Size: 774 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 159 KiB |
@@ -0,0 +1,34 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import './certificates.css'
|
||||
|
||||
function Certificates() {
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" })
|
||||
}, [])
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="cert-container">
|
||||
<div className="img-container1">
|
||||
<img src="/sertifikat1.jpg" alt="Image 1" />
|
||||
</div>
|
||||
<div className="img-container1">
|
||||
<img src="sertifikat2.jpg" alt="Image 2" />
|
||||
</div>
|
||||
{/* <div className="img-container1">
|
||||
<img src="sertifikat3.jpg" alt="Image 3" />
|
||||
</div>
|
||||
<div className="img-container1">
|
||||
<img src="sertifikat4" alt="Image 4" />
|
||||
</div> */}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Certificates
|
||||
@@ -0,0 +1,30 @@
|
||||
.cert-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-around;
|
||||
margin-top: 150px;
|
||||
}
|
||||
|
||||
.img-container1 {
|
||||
width: 40vw;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1050px) {
|
||||
.cert-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
/* gap: 50px; */
|
||||
flex-wrap: wrap;
|
||||
margin-top: 50px;
|
||||
margin-left: 3vw;
|
||||
}
|
||||
|
||||
.img-container1 {
|
||||
width: 80vw;
|
||||
height: 40vh;
|
||||
/* margin-top: 20px; */
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { getAllUsers, uploadDocument } from '../../services/api';
|
||||
|
||||
function AdminPanel() {
|
||||
const [users, setUsers] = useState([]);
|
||||
const [file, setFile] = useState(null);
|
||||
const [title, setTitle] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
fetchUsers();
|
||||
}, []);
|
||||
|
||||
const fetchUsers = async () => {
|
||||
try {
|
||||
const response = await getAllUsers();
|
||||
setUsers(response.data);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch users:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileUpload = async (e) => {
|
||||
e.preventDefault();
|
||||
if (!file) return;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('title', title);
|
||||
|
||||
try {
|
||||
await uploadDocument(formData);
|
||||
alert('Document uploaded successfully');
|
||||
} catch (error) {
|
||||
console.error('Failed to upload document:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Users</h2>
|
||||
<ul>
|
||||
{users.map(user => (
|
||||
<li key={user.id}>{user.name} ({user.email})</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<h2>Upload Document</h2>
|
||||
<form onSubmit={handleFileUpload}>
|
||||
<input
|
||||
type="text"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
placeholder="Document Title"
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="file"
|
||||
onChange={(e) => setFile(e.target.files[0])}
|
||||
required
|
||||
/>
|
||||
<button type="submit">Upload</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AdminPanel;
|
||||
|
||||
// import { useState } from 'react';
|
||||
// import { uploadDocument } from '../../services/api';
|
||||
|
||||
// const AdminPanel = () => {
|
||||
// const [file, setFile] = useState(null);
|
||||
// const [progress, setProgress] = useState(0);
|
||||
// const [title, setTitle] = useState('');
|
||||
// const [content, setContent] = useState('');
|
||||
// const [isUploading, setIsUploading] = useState(false);
|
||||
|
||||
// const handleFileChange = (event) => {
|
||||
// if (event.target.files) {
|
||||
// setFile(event.target.files[0]);
|
||||
// }
|
||||
// };
|
||||
|
||||
// const handleSubmit = async (event) => {
|
||||
// event.preventDefault();
|
||||
// if (!file || !title) {
|
||||
// alert('Please select a file and enter a title');
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const formData = new FormData();
|
||||
// formData.append('file', file);
|
||||
// formData.append('title', title);
|
||||
// formData.append('content', content);
|
||||
|
||||
// setIsUploading(true);
|
||||
// try {
|
||||
// await uploadDocument(formData, (progressEvent) => {
|
||||
// const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
||||
// setProgress(percentCompleted);
|
||||
// });
|
||||
|
||||
// alert('Document uploaded successfully!');
|
||||
// setProgress(0);
|
||||
// setFile(null);
|
||||
// setTitle('');
|
||||
// setContent('');
|
||||
// } catch (error) {
|
||||
// console.error('Upload failed:', error);
|
||||
// alert('Upload failed. Please try again.');
|
||||
// } finally {
|
||||
// setIsUploading(false);
|
||||
// setProgress(0);
|
||||
// }
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <div className="admin-panel">
|
||||
// <h2>Admin Panel - Upload Document</h2>
|
||||
// <form onSubmit={handleSubmit}>
|
||||
// <div>
|
||||
// <label htmlFor="title">Document Title:</label>
|
||||
// <input
|
||||
// type="text"
|
||||
// id="title"
|
||||
// value={title}
|
||||
// onChange={(e) => setTitle(e.target.value)}
|
||||
// required
|
||||
// />
|
||||
// </div>
|
||||
// <div>
|
||||
// <label htmlFor="content">Document Content:</label>
|
||||
// <textarea
|
||||
// id="content"
|
||||
// value={content}
|
||||
// onChange={(e) => setContent(e.target.value)}
|
||||
// rows="4"
|
||||
// />
|
||||
// </div>
|
||||
// <div>
|
||||
// <label htmlFor="file">Select File:</label>
|
||||
// <input
|
||||
// type="file"
|
||||
// id="file"
|
||||
// onChange={handleFileChange}
|
||||
// required
|
||||
// />
|
||||
// </div>
|
||||
// <button type="submit" disabled={isUploading}>
|
||||
// {isUploading ? 'Uploading...' : 'Upload Document'}
|
||||
// </button>
|
||||
// {progress > 0 && (
|
||||
// <div>
|
||||
// <progress value={progress} max="100" />
|
||||
// <p>{progress}% uploaded</p>
|
||||
// </div>
|
||||
// )}
|
||||
// </form>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default AdminPanel;
|
||||
@@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
function Clients() {
|
||||
return (
|
||||
<div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Clients
|
||||
@@ -0,0 +1,23 @@
|
||||
.anotherContainer {
|
||||
width: 80vw;
|
||||
margin-left: 10vw;
|
||||
text-align: justify;
|
||||
/* text-align: center; */
|
||||
/* font-size: 1.3rem; */
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media screen and (max-width: 1050px) {
|
||||
|
||||
.constaltingContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.anotherContainer {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { useEffect } from 'react'
|
||||
import './consalting.css'
|
||||
|
||||
export default function Consulting() {
|
||||
useEffect(() => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" })
|
||||
}, [])
|
||||
return (
|
||||
<>
|
||||
<div className="constaltingContainer">
|
||||
<h1 className="text-4xl text-center mt-32 font-bold tracking-tight text-gray-900 sm:text-3xl">Преглед на консултантски услуги</h1>
|
||||
<div className="anotherContainer text-center">
|
||||
<p className="tracking-tight text-gray-900 text-center sm:text-3xl mt-16">
|
||||
ИМК нуди низа консултантски услуги, предлагајќи комплексен пристап за поддршка на различни аспекти на индустриските операции. Услугите вклучуваат изработка на интегрирани дозволи, проценки на влијанието врз животната средина, проектна документација и студии за финансиска исплатливост на индустриските процеси.
|
||||
|
||||
</p>
|
||||
<ul className="tracking-tight text-gray-900 sm:text-2xl mt-16 text-center md:text-justify">
|
||||
<li><strong>Интегрирани Дозволи (А и Б):</strong> Компанијата нуди услуги поврзани со изработка на интегрирани дозволи. Ова покажува експертиза во справувањето со регулаторните рамки и обезбедување на согласност со лиценцните барања. Интегрираните дозволи се суштински за индустриските операции, што укажува на фокус на оптимизација на правните и регулаторни процеси.</li>
|
||||
<li><strong>Проценка на Влијанието врз Животната Средина:</strong> Консултантот обезбедува услуги за проценка на влијанието врз животната средина. Ова вклучува систематска евалуација на тоа како индустриските активности можат да влијаат на околната средина. Таквите проценки се суштински за обезбедување на одржливи практики и согласност со еколошките регулативи.</li>
|
||||
<li><strong>Проектна Документација за Производствени Објекти:</strong> ИМК нуди изработка на проектна документација специфично дизајнирана за производствени објекти, земајки ги предвид различните аспекти на дизајнирање и оптимизација на индустриските операции.</li>
|
||||
<li><strong>Проекти за Финасиска Ефикасност за Производствени Објекти:</strong> ИМК нуди експертиза во креирање на ценовна ефикасност за производствени капацитети. Ова вклучува анализа на постоечките процеси и идентификација на можностите за оптимизација за подобрување на продуктивноста и намалување на оперативните трошоци. Фокусот на ценовната ефикасност покажува посветеност кон подобрување на општата финансиска перформанса на индустриските операции.</li>
|
||||
<li><strong>Елаборати за Финансиска Исплатливост на Производствени Процеси:</strong> ИМК изработува елаборати (детални извештаи) фокусирани на проценка на финансиската исплатливост на производствените процеси. Тие ги вклучуваат економските можности и оправданоста/одржливоста на проектираните или постоечките производствени процеси.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getUserInfo } from '../../services/api';
|
||||
import * as jwtDecode from 'jwt-decode';
|
||||
|
||||
function Dashboard() {
|
||||
const [userInfo, setUserInfo] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetchUserInfo();
|
||||
}, []);
|
||||
|
||||
const fetchUserInfo = async () => {
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
const decodedToken = jwtDecode.jwtDecode(token);
|
||||
const response = await getUserInfo(decodedToken.sub);
|
||||
setUserInfo(response.data);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch user info:', error);
|
||||
}
|
||||
};
|
||||
|
||||
if (!userInfo) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="dashboard">
|
||||
<h1>Welcome, {userInfo.name}!</h1>
|
||||
<p>Email: {userInfo.email}</p>
|
||||
|
||||
<h2>Quick Links</h2>
|
||||
<ul>
|
||||
<li><Link to="/documents">My Documents</Link></li>
|
||||
<li><Link to="/upload">Upload Document</Link></li>
|
||||
{userInfo.isAdmin && <li><Link to="/admin">Admin Panel</Link></li>}
|
||||
</ul>
|
||||
|
||||
<h2>Recent Activity</h2>
|
||||
{/* You can add a list of recent activities here */}
|
||||
<p>No recent activity to display.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Dashboard;
|
||||
@@ -0,0 +1,4 @@
|
||||
footer {
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import './footer.css'
|
||||
|
||||
const Footer = () => {
|
||||
return (
|
||||
<>
|
||||
<footer
|
||||
className="bg-neutral-100 text-center text-neutral-600 dark:bg-neutral-600 dark:text-neutral-200 lg:text-left">
|
||||
|
||||
|
||||
<div className="mx-6 py-10 text-center md:text-left">
|
||||
<div className="grid-1 grid gap-8 md:grid-cols-2 lg:grid-cols-4">
|
||||
<div className="">
|
||||
<h6
|
||||
className="mb-4 flex items-center justify-center font-semibold uppercase md:justify-start">
|
||||
|
||||
Испитување материјали и консултантство
|
||||
</h6>
|
||||
<p>
|
||||
Вашиот партнер во контролата на безбедно градење и квалитетно живеење
|
||||
</p>
|
||||
</div>
|
||||
<div className="">
|
||||
<h6
|
||||
className="mb-4 flex justify-center font-semibold uppercase md:justify-start">
|
||||
Услуги
|
||||
</h6>
|
||||
<NavLink to={'lab'}>
|
||||
<p className="mb-4">
|
||||
<a href="#!" className="text-neutral-600 dark:text-neutral-200"
|
||||
>Лабораториски анализи</a
|
||||
>
|
||||
</p>
|
||||
</NavLink>
|
||||
<NavLink to={'ultrasound'}>
|
||||
<p className="mb-4">
|
||||
<a href="#!" className="text-neutral-600 dark:text-neutral-200"
|
||||
>Испитување со ултразвук</a
|
||||
>
|
||||
</p>
|
||||
</NavLink>
|
||||
<NavLink to={'consulting'}>
|
||||
<p className="mb-4">
|
||||
<a href="#!" className="text-neutral-600 dark:text-neutral-200"
|
||||
>Консултатски услуги</a
|
||||
>
|
||||
</p>
|
||||
</NavLink>
|
||||
</div>
|
||||
<div className="">
|
||||
<h6
|
||||
className="mb-4 flex justify-center font-semibold uppercase md:justify-start">
|
||||
Корисни линкови
|
||||
</h6>
|
||||
<p className="mb-4">
|
||||
<a href="#!" className="text-neutral-600 dark:text-neutral-200"
|
||||
>Безбедно градење</a
|
||||
>
|
||||
</p>
|
||||
<p className="mb-4">
|
||||
<a href="#!" className="text-neutral-600 dark:text-neutral-200"
|
||||
>Контрола на квалитет</a
|
||||
>
|
||||
</p>
|
||||
<NavLink to="/certificates" >
|
||||
<p className="mb-4">
|
||||
<a>Сертификати</a>
|
||||
</p>
|
||||
</NavLink>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<h6
|
||||
className="mb-4 flex justify-center font-semibold uppercase md:justify-start">
|
||||
Контакт
|
||||
</h6>
|
||||
<p className="mb-4 flex items-center justify-center md:justify-start">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
className="mr-3 h-5 w-5">
|
||||
<path
|
||||
d="M11.47 3.84a.75.75 0 011.06 0l8.69 8.69a.75.75 0 101.06-1.06l-8.689-8.69a2.25 2.25 0 00-3.182 0l-8.69 8.69a.75.75 0 001.061 1.06l8.69-8.69z" />
|
||||
<path
|
||||
d="M12 5.432l8.159 8.159c.03.03.06.058.091.086v6.198c0 1.035-.84 1.875-1.875 1.875H15a.75.75 0 01-.75-.75v-4.5a.75.75 0 00-.75-.75h-3a.75.75 0 00-.75.75V21a.75.75 0 01-.75.75H5.625a1.875 1.875 0 01-1.875-1.875v-6.198a2.29 2.29 0 00.091-.086L12 5.43z" />
|
||||
</svg>
|
||||
16-та Македонска бригада бр. 18
|
||||
</p>
|
||||
<p className="mb-4 flex items-center justify-center md:justify-start">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
className="mr-3 h-5 w-5">
|
||||
<path
|
||||
d="M1.5 8.67v8.58a3 3 0 003 3h15a3 3 0 003-3V8.67l-8.928 5.493a3 3 0 01-3.144 0L1.5 8.67z" />
|
||||
<path
|
||||
d="M22.5 6.908V6.75a3 3 0 00-3-3h-15a3 3 0 00-3 3v.158l9.714 5.978a1.5 1.5 0 001.572 0L22.5 6.908z" />
|
||||
</svg>
|
||||
stanko@imk.mk
|
||||
</p>
|
||||
<p className="mb-4 flex items-center justify-center md:justify-start">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
className="mr-3 h-5 w-5">
|
||||
<path
|
||||
d="M1.5 8.67v8.58a3 3 0 003 3h15a3 3 0 003-3V8.67l-8.928 5.493a3 3 0 01-3.144 0L1.5 8.67z" />
|
||||
<path
|
||||
d="M22.5 6.908V6.75a3 3 0 00-3-3h-15a3 3 0 00-3 3v.158l9.714 5.978a1.5 1.5 0 001.572 0L22.5 6.908z" />
|
||||
</svg>
|
||||
info.imkmk@gmail.com
|
||||
</p>
|
||||
<p className="mb-4 flex items-center justify-center md:justify-start">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
className="mr-3 h-5 w-5">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M1.5 4.5a3 3 0 013-3h1.372c.86 0 1.61.586 1.819 1.42l1.105 4.423a1.875 1.875 0 01-.694 1.955l-1.293.97c-.135.101-.164.249-.126.352a11.285 11.285 0 006.697 6.697c.103.038.25.009.352-.126l.97-1.293a1.875 1.875 0 011.955-.694l4.423 1.105c.834.209 1.42.959 1.42 1.82V19.5a3 3 0 01-3 3h-2.25C8.552 22.5 1.5 15.448 1.5 6.75V4.5z"
|
||||
clipRule="evenodd" />
|
||||
</svg>
|
||||
+ 389 70 279 877
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-neutral-200 p-6 text-center dark:bg-neutral-700">
|
||||
<span>© 2023 Copyright:</span>
|
||||
<a
|
||||
className="font-semibold text-neutral-600 dark:text-neutral-400"
|
||||
href="https://tailwind-elements.com/"
|
||||
> IMK</a
|
||||
>
|
||||
</div>
|
||||
</footer >
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Footer
|
||||
@@ -0,0 +1,42 @@
|
||||
import React from 'react'
|
||||
import './gallery.css'
|
||||
import { useEffect } from 'react';
|
||||
function Gallery() {
|
||||
useEffect(() => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" })
|
||||
}, [])
|
||||
return (
|
||||
<>
|
||||
|
||||
<div className="gallery-container mt-32">
|
||||
<div className="img-container">
|
||||
<img src="1.jpg" alt="Image 1" className="hover:scale-110" />
|
||||
</div>
|
||||
<div className="img-container">
|
||||
<img src="2.jpg" alt="Image 2" className="hover:scale-110" />
|
||||
</div>
|
||||
<div className="img-container">
|
||||
<img src="3.jpg" alt="Image 3" className="hover:scale-110" />
|
||||
</div>
|
||||
<div className="img-container">
|
||||
<img src="4.jpg" alt="Image 4" className="hover:scale-110" />
|
||||
</div>
|
||||
<div className="img-container">
|
||||
<img src="5.jpg" alt="Image 5" className="hover:scale-110" />
|
||||
</div>
|
||||
<div className="img-container">
|
||||
<img src="6.jpg" alt="Image 6" className="hover:scale-110" />
|
||||
</div>
|
||||
<div className="img-container">
|
||||
<img src="8.jpg" alt="Image 8" className="hover:scale-110" />
|
||||
</div><div className="img-container">
|
||||
<img src="9.jpg" alt="Image 9" className="hover:scale-110" />
|
||||
</div><div className="img-container">
|
||||
<img src="10.jpg" alt="Image 10" className="hover:scale-110" />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Gallery
|
||||
@@ -0,0 +1,30 @@
|
||||
.gallery-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 50px;
|
||||
margin-left: 2vw;
|
||||
}
|
||||
|
||||
.img-container img {
|
||||
width: 30vw;
|
||||
height: 40vh;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1050px) {
|
||||
.gallery-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 50px;
|
||||
margin-left: 3vw;
|
||||
}
|
||||
|
||||
.img-container img {
|
||||
width: 80vw;
|
||||
height: 40vh;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
.textBox {
|
||||
margin-top: 120px;
|
||||
margin-bottom: 50px;
|
||||
padding: 70px;
|
||||
width: 40vw;
|
||||
box-shadow: -1px 5px 20px 1px rgba(0, 0, 0, 0.34);
|
||||
-webkit-box-shadow: -1px 5px 20px 1px rgba(0, 0, 0, 0.34);
|
||||
-moz-box-shadow: -1px 5px 20px 1px rgba(0, 0, 0, 0.34);
|
||||
/* text-align: left;*/
|
||||
}
|
||||
|
||||
.imgBox {
|
||||
width: 30vw;
|
||||
box-shadow: -1px 5px 20px 1px rgba(0, 0, 0, 0.34);
|
||||
-webkit-box-shadow: -1px 5px 20px 1px rgba(0, 0, 0, 0.34);
|
||||
-moz-box-shadow: -1px 5px 20px 1px rgba(0, 0, 0, 0.34);
|
||||
}
|
||||
|
||||
|
||||
.text {
|
||||
/* margin-bottom: 10px; */
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 50px;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.text h1 {
|
||||
font-size: 1.5rem;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.textContainer {
|
||||
/* display: flex; */
|
||||
background-image: url("../../../public/background.jpg");
|
||||
}
|
||||
|
||||
.heading {
|
||||
text-transform: capitalize;
|
||||
text-align: center;
|
||||
font-size: 1.7rem;
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1050px) {
|
||||
.imgBox {
|
||||
display: none;
|
||||
/* width: 100vw; */
|
||||
}
|
||||
|
||||
.textBox {
|
||||
display: block;
|
||||
width: 80vw;
|
||||
margin-bottom: -150px;
|
||||
text-align: center;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 300px;
|
||||
align-items: center;
|
||||
margin-bottom: 75px;
|
||||
}
|
||||
|
||||
#geotehnika {
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 250px;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
import { useEffect } from "react";
|
||||
import "./lab.css";
|
||||
|
||||
export default function Lab() {
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" })
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="wrapper">
|
||||
<div className="heading sm:mb-5 md:mb-32">
|
||||
<h1>во нашата лабораторија се вршат</h1>
|
||||
</div>
|
||||
|
||||
<div className="text">
|
||||
<div className="group md:bg-slate-300 sm:bg-none">
|
||||
<div className="textBox md:ml-10 bg-slate-100 sm:ml-0">
|
||||
<p>
|
||||
<strong>Испитувањето на бетон</strong> е важен процес кој се
|
||||
користи за да се оцени квалитетот и издржливоста на бетонски
|
||||
конструкции.
|
||||
</p>
|
||||
<p>
|
||||
Испитувањето на бетонот е неопходен процес за осигурување на
|
||||
квалитет и безбедност на градежните проекти и инфраструктурата
|
||||
воопшто. Тоа помага во отстранивање на недостатоците и
|
||||
намалување на ризиците од евентуални пропаднувања на
|
||||
конструкциите.
|
||||
</p>
|
||||
</div>
|
||||
<div className="imgBox md:mr-10 my-10 sm:mr-0">
|
||||
<img src="/kocki.webp" alt="" loading="lazy" />
|
||||
</div>
|
||||
</div>
|
||||
<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"
|
||||
/>
|
||||
</div>
|
||||
<div className="textBox md:mr-10 bg-slate-100 sm:mr-0">
|
||||
|
||||
<p>
|
||||
<strong>Геотехничките испитувања</strong> се однесуваат на
|
||||
процесот на испитување и анализирање на геолошките и
|
||||
геотехничките својства на земјиштето за да се одреди неговата
|
||||
способност да ги поддржат инфраструктурните конструкции и
|
||||
проекти.
|
||||
</p>
|
||||
<p>
|
||||
Геотехничките испитувања помагаат во разбирањето на
|
||||
геотехничките услови на градежното место и обезбедуваат
|
||||
информации за дизајнирање и изградба на стабилни и издржливи
|
||||
инфраструктурни објекти и конструкции.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="group md:bg-slate-300 sm:bg-none">
|
||||
<div className="textBox md:ml-10 bg-slate-100 sm:ml-0">
|
||||
<p>
|
||||
<strong>Испитувањето на руди, метали и троски </strong>е важен
|
||||
процес во областа на геолошкото и металуршко инженерство, со цел
|
||||
да се анализираат и одредат својствата и составот на суровините
|
||||
и производите.
|
||||
</p>
|
||||
<p>
|
||||
Овие испитувања се изведуваат за да се осигура квалитетот и
|
||||
спецификациите на материјалите, како и за да се обезбеди
|
||||
безбедно и ефикасно користење и обработка на руди, метали и
|
||||
троски во различни индустрии и апликации.
|
||||
</p>
|
||||
</div>
|
||||
<div className="imgBox md:mr-10 my-10 sm:mr-0">
|
||||
<img id="geotehnika" src="/lab.webp" alt="" loading="lazy" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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="/armatura.jpg" alt="" loading="lazy" />
|
||||
</div>
|
||||
|
||||
<div className="textBox md:mr-10 bg-slate-100 sm:mr-0">
|
||||
<p>
|
||||
<strong>Испитувањето на челик </strong>е важен процес за
|
||||
одредување на квалитетот, издржливоста и други механички
|
||||
својства на челикот, кој се користи во разни индустрии и
|
||||
конструкции.
|
||||
</p>
|
||||
<p>
|
||||
Испитувањето на челик е неопходен процес за да се обезбеди
|
||||
квалитетот и безбедноста на производите и конструкциите кои го
|
||||
користат, како и за да се уверите дека челикот одговара на
|
||||
спецификациите и стандардите.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="group md:bg-slate-300 sm:bg-none">
|
||||
<div className="textBox md:ml-10 bg-slate-100 sm:ml-0">
|
||||
<p>
|
||||
<strong>Лабораториската екстракција</strong> на метали или
|
||||
неметали од руди и троски е процес кој се користи за да се
|
||||
изолираат и оддели материјалите од суровините или отпадните
|
||||
производи, како што се рудите или троските, со цел да се добие
|
||||
чист метал или неметал.
|
||||
</p>
|
||||
</div>
|
||||
<div className="imgBox md:mr-10 my-10 sm:mr-0">
|
||||
<img id="geotehnika" src="/ekstrakcija.jpg" alt="" loading="lazy" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { login } from '../../services/api';
|
||||
|
||||
function Login() {
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
const response = await login(username, password);
|
||||
localStorage.setItem('token', response.data.access_token);
|
||||
navigate('/dashboard');
|
||||
} catch (error) {
|
||||
console.error('Login failed:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
placeholder="Username"
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="Password"
|
||||
required
|
||||
/>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default Login;
|
||||
@@ -0,0 +1,123 @@
|
||||
import { useState } from "react";
|
||||
import { Dialog } from "@headlessui/react";
|
||||
import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import "./navbar.css";
|
||||
const navigation = [
|
||||
{ name: "Дома", href: "/" },
|
||||
{ name: "За нас", href: "/about" },
|
||||
{ name: "Галерија", href: "/gallery" },
|
||||
{ name: "Контакт", href: "/contact" },
|
||||
{ name: "Клиенти", href: "/clients" },
|
||||
{ name: "Админ", href: "/admin" },
|
||||
{ name: "Логин", href: "/login" },
|
||||
];
|
||||
|
||||
export default function Navbar() {
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="">
|
||||
<div className="bg-white">
|
||||
<header className="nav">
|
||||
<nav
|
||||
className="flex items-center justify-between p-6 lg:px-8"
|
||||
aria-label="Global"
|
||||
>
|
||||
<div id="logo" 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="/imklogorgb.png"
|
||||
alt="logo"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex lg:hidden">
|
||||
<button
|
||||
type="button"
|
||||
className="-m-2.5 inline-flex items-center justify-center rounded-md p-2.5 text-gray-700"
|
||||
onClick={() => setMobileMenuOpen(true)}
|
||||
>
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
.nav {
|
||||
background-color: rgb(228, 237, 240);
|
||||
box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.34);
|
||||
-webkit-box-shadow: -1px 5px 20px 1px rgba(0, 0, 0, 0.34);
|
||||
-moz-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.34);
|
||||
background-image: url("../public/background.jpg");
|
||||
}
|
||||
|
||||
/* #logo {
|
||||
margin-left: 10px;
|
||||
} */
|
||||
|
||||
/* #logo-tuf {
|
||||
margin-right: 20px;
|
||||
} */
|
||||
|
||||
a:hover {
|
||||
border-bottom: 1px solid gray;
|
||||
}
|
||||
|
||||
.navitems {
|
||||
/* margin-right: 100px; */
|
||||
margin-left: 100px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1050px) {
|
||||
#logo {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
#logo img {
|
||||
width: 70px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
#logo-tuf {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Navigate, useLocation } from 'react-router-dom';
|
||||
import { useAuth } from '../../hooks/useAuth';
|
||||
// eslint-disable-next-line react/prop-types
|
||||
function ProtectedRoute({ children, adminOnly = false }) {
|
||||
const { user, loading } = useAuth();
|
||||
const location = useLocation();
|
||||
|
||||
if (loading) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return <Navigate to="/login" state={{ from: location }} replace />;
|
||||
}
|
||||
|
||||
if (adminOnly && !user.isAdmin) {
|
||||
return <Navigate to="/dashboard" replace />;
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
export default ProtectedRoute;
|
||||
@@ -0,0 +1,3 @@
|
||||
.insuranceContainer {
|
||||
height: 100vh;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import './ultraSound.css'
|
||||
|
||||
export default function UltraSound() {
|
||||
useEffect(() => {
|
||||
window.scrollTo({ top: -100, behavior: "smooth" })
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="ultraSoundContainer flex justify-around mt-40">
|
||||
<iframe width="1120" height="630" src="https://www.youtube.com/embed/WGjG8XWS9RQ?si=fE2aL5eZ-5ivuuxV" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { getUserInfo } from '../services/api';
|
||||
import * as jwtDecode from 'jwt-decode';
|
||||
|
||||
export function useAuth() {
|
||||
const [user, setUser] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchUser() {
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
const decodedToken = jwtDecode.jwtDecode(token);
|
||||
const response = await getUserInfo(decodedToken.sub);
|
||||
setUser(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch user info:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
fetchUser();
|
||||
}, []);
|
||||
|
||||
return { user, loading };
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -0,0 +1,10 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
@@ -0,0 +1,124 @@
|
||||
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>
|
||||
|
||||
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
.aboutGroup {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.aboutContainer {
|
||||
width: 40vw;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.aboutContainer p {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (max-width: 1050px) {
|
||||
.aboutGroup {
|
||||
display: block;
|
||||
|
||||
}
|
||||
|
||||
.aboutContainer {
|
||||
width: 80vw;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
|
||||
}
|
||||
|
||||
.form-container {
|
||||
margin-top: 150px;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
const Cloud = () => {
|
||||
return (
|
||||
<div>
|
||||
<h1>Cloud Page</h1>
|
||||
<link rel="stylesheet" href="https://example.com/styles.css" />
|
||||
{/* Rest of your code */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Cloud;
|
||||
@@ -0,0 +1,115 @@
|
||||
|
||||
import { useState } from 'react'
|
||||
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
||||
import { Switch } from '@headlessui/react'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default function Contact() {
|
||||
const [agreed, setAgreed] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="isolate bg-gray px-6 py-24 sm:py-32 lg:px-8">
|
||||
<div
|
||||
aria-hidden="true"
|
||||
>
|
||||
|
||||
</div>
|
||||
<div className="mx-auto max-w-2xl text-center">
|
||||
<h2 className="text-3xl font-bold tracking-tight text-gray-900 sm:text-3xl">Пишете ни порака | Закажете средба</h2>
|
||||
{/* <p className="mt-2 text-lg leading-8 text-gray-600">
|
||||
take a wheel or let it slide
|
||||
|
||||
</p> */}
|
||||
</div>
|
||||
<form action="https://formsubmit.co/taratur@gmail.com" 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="company" className="block text-sm font-semibold leading-6 text-gray-900">
|
||||
Компанија
|
||||
</label>
|
||||
<div className="mt-2.5">
|
||||
<input
|
||||
type="text"
|
||||
name="company"
|
||||
id="company"
|
||||
autoComplete="organization"
|
||||
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>
|
||||
<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>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
import { NavLink } from "react-router-dom";
|
||||
import "./home.css";
|
||||
|
||||
const Home = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="homeContainer">
|
||||
<div className="bannerTop">
|
||||
<div className="slogan md:bg-slate-300 w-full py-10 sm:bg-none">
|
||||
<h1 className="">
|
||||
ИМК: Вашиот партнер во контролата на безбедно градење и квалитетно
|
||||
живеење
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="imgHelper">
|
||||
<div className="">
|
||||
<div className="grid grid-cols-1 grid-rows-1 md:grid-cols-3 gap-16 mt-16">
|
||||
<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">
|
||||
<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="/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">
|
||||
Some quick example text to build on the card title and make
|
||||
up the bulk of the card's content.
|
||||
</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>
|
||||
</div>
|
||||
</NavLink>
|
||||
|
||||
|
||||
<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="/consulting">
|
||||
<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="/consulting2.jpg"
|
||||
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">
|
||||
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 className="bannerBottom mt-16">
|
||||
<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>
|
||||
|
||||
</div>
|
||||
</div >
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
@@ -0,0 +1,105 @@
|
||||
.homeContainer {
|
||||
width: 80vw;
|
||||
margin-left: 10vw;
|
||||
}
|
||||
|
||||
.bannerTop {
|
||||
padding: 50px;
|
||||
font-size: 1.5rem;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.bannerBottom h2 {
|
||||
font-size: 1.5rem;
|
||||
text-align: center;
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
.someclass {
|
||||
margin-top: 50px;
|
||||
margin-bottom: 30px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-bottom: 150px;
|
||||
}
|
||||
|
||||
.someclass img {
|
||||
width: 70vw;
|
||||
/* height: 500px; */
|
||||
}
|
||||
|
||||
.someclass h1 {
|
||||
font-size: 1.5rem;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.listCards {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
gap: 20px;
|
||||
/* flex-wrap: wrap; */
|
||||
}
|
||||
|
||||
.serviceCard {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 100px;
|
||||
width: 25vw;
|
||||
border: 1px solid black;
|
||||
padding: 20px;
|
||||
border-radius: 30px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.serviceCard h1 {
|
||||
/* border-bottom: 1px solid black; */
|
||||
font-size: 1.5rem;
|
||||
padding: 10px;
|
||||
/* text-decoration: underline; */
|
||||
}
|
||||
|
||||
.imgHelper img {
|
||||
height: 300px !important;
|
||||
/* width: fit-content; */
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: inside;
|
||||
padding-left: 10px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.slogan {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1050px) {
|
||||
.listCards {
|
||||
display: block;
|
||||
/* width: 80vw; */
|
||||
}
|
||||
|
||||
.serviceCard {
|
||||
width: 80vw;
|
||||
margin-left: 0.5vw;
|
||||
}
|
||||
|
||||
.imgHelper img {
|
||||
width: fit;
|
||||
}
|
||||
|
||||
.homeContainer {
|
||||
width: 80vw;
|
||||
margin-left: 3vw;
|
||||
}
|
||||
|
||||
.bannerTop {
|
||||
margin-top: -20px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import axios from 'axios';
|
||||
|
||||
|
||||
const API_URL = 'http://localhost:3000';
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: API_URL,
|
||||
});
|
||||
|
||||
api.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
export const createUser = (userData) => api.post('/admin/users', userData);
|
||||
export const login = (username, password) => api.post('/auth/login', { username, password });
|
||||
export const getAllUsers = () => api.get('/admin/users');
|
||||
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 uploadDocument = (formData) => api.post('/admin/documents', formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
});
|
||||
export const getUserInfo = () => api.get('/auth/user-info');
|
||||
|
||||
export default api;
|
||||
@@ -0,0 +1,56 @@
|
||||
import { useState } from 'react';
|
||||
import axios from 'axios';
|
||||
|
||||
const FileUpload = () => {
|
||||
const [file, setFile] = useState(null);
|
||||
const [progress, setProgress] = useState(0);
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
if (event.target.files) {
|
||||
setFile(event.target.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
if (!file) return;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('title', 'Your document title');
|
||||
// Add other fields as needed
|
||||
|
||||
try {
|
||||
await axios.post('/api/admin/document', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
onUploadProgress: (progressEvent) => {
|
||||
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
||||
setProgress(percentCompleted);
|
||||
},
|
||||
});
|
||||
|
||||
console.log('Upload successful');
|
||||
setProgress(0);
|
||||
} catch (error) {
|
||||
console.error('Upload failed:', error);
|
||||
setProgress(0);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input type="file" onChange={handleFileChange} />
|
||||
<button type="submit">Upload</button>
|
||||
{progress > 0 && (
|
||||
<div>
|
||||
<progress value={progress} max="100" />
|
||||
<p>{progress}% uploaded</p>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default FileUpload;
|
||||
@@ -0,0 +1,8 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||