This commit is contained in:
Dimitar765 committed 2024-10-28 18:43:22 +01:00
1 parent 2b9dad1fbf
commit 8cbdde39c1
139 files changed
+19353

No files matched your search

+20
View File
@@ -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 },
],
},
}
+9
View File
@@ -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
+16
View File
@@ -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>
+4437
View File
File diff suppressed because it is too large. Load diff
+34
View File
@@ -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"
}
}
+6
View File
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1003 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

+56
View File
@@ -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);
}
+53
View File
@@ -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
Binary file not shown.

After

Width:  |  Height:  |  Size: 774 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

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;
}
}
+83
View File
@@ -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;
}
+122
View File
@@ -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>
</>
)
}
+29
View File
@@ -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 };
}
+3
View File
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
+10
View File
@@ -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>,
)
+124
View File
@@ -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;
}
}
+13
View File
@@ -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>
)
}
+235
View File
@@ -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;
+105
View File
@@ -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;
}
}
+27
View File
@@ -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;
+8
View File
@@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [],
};
+7
View File
@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})