Spaces:
Sleeping
Sleeping
Upload 46 files
Browse files- README.md +10 -10
- __pycache__/image_routes.cpython-312.pyc +0 -0
- __pycache__/models.cpython-312.pyc +0 -0
- static/js/analysis.js +10 -0
- static/js/auth.js +47 -3
- static/js/dashboard.js +6 -0
- static/js/main.js +5 -12
- static/js/menu.js +5 -0
- static/js/navigation.js +14 -1
- templates/base.html +19 -19
- templates/components/header.html +3 -23
- templates/components/mobile_menu.html +26 -0
README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: My Flask App
|
| 3 |
-
emoji: 🚀
|
| 4 |
-
colorFrom: green
|
| 5 |
-
colorTo: yellow
|
| 6 |
-
sdk: docker
|
| 7 |
-
pinned: false
|
| 8 |
-
---
|
| 9 |
-
|
| 10 |
-
# Flask app running with Docker on Hugging Face Spaces
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: My Flask App
|
| 3 |
+
emoji: 🚀
|
| 4 |
+
colorFrom: green
|
| 5 |
+
colorTo: yellow
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
# Flask app running with Docker on Hugging Face Spaces
|
__pycache__/image_routes.cpython-312.pyc
CHANGED
|
Binary files a/__pycache__/image_routes.cpython-312.pyc and b/__pycache__/image_routes.cpython-312.pyc differ
|
|
|
__pycache__/models.cpython-312.pyc
CHANGED
|
Binary files a/__pycache__/models.cpython-312.pyc and b/__pycache__/models.cpython-312.pyc differ
|
|
|
static/js/analysis.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
| 1 |
import { getCurrentLanguage, translate } from './i18n.js';
|
| 2 |
import { getCurrentImageFile } from './upload.js';
|
|
|
|
|
|
|
| 3 |
|
| 4 |
async function analyzeImage() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
const currentImageFile = getCurrentImageFile();
|
| 6 |
if (!currentImageFile) {
|
| 7 |
alert(getCurrentLanguage() === 'az' ? 'Zəhmət olmasa əvvəlcə şəkil seçin' : 'Please select an image first');
|
|
|
|
| 1 |
import { getCurrentLanguage, translate } from './i18n.js';
|
| 2 |
import { getCurrentImageFile } from './upload.js';
|
| 3 |
+
import { apiCall } from './auth.js';
|
| 4 |
+
import { navigateTo } from './navigation.js';
|
| 5 |
|
| 6 |
async function analyzeImage() {
|
| 7 |
+
// Check authentication first
|
| 8 |
+
const authResult = await apiCall('/api/check-auth');
|
| 9 |
+
if (!authResult.success || !authResult.data.authenticated) {
|
| 10 |
+
alert(getCurrentLanguage() === 'az' ? 'Zəhmət olmasa əvvəlcə daxil olun' : 'Please sign in first to analyze images');
|
| 11 |
+
navigateTo('login', { preventDefault: () => {} });
|
| 12 |
+
return;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
const currentImageFile = getCurrentImageFile();
|
| 16 |
if (!currentImageFile) {
|
| 17 |
alert(getCurrentLanguage() === 'az' ? 'Zəhmət olmasa əvvəlcə şəkil seçin' : 'Please select an image first');
|
static/js/auth.js
CHANGED
|
@@ -37,6 +37,9 @@ function toggleAccountMenu() {
|
|
| 37 |
function updateUIForLoggedIn(email) {
|
| 38 |
const signInBtn = document.getElementById('sign-in-btn');
|
| 39 |
const accountMenu = document.getElementById('account-menu');
|
|
|
|
|
|
|
|
|
|
| 40 |
if (signInBtn) {
|
| 41 |
signInBtn.textContent = email || 'Dashboard';
|
| 42 |
signInBtn.onclick = (event) => {
|
|
@@ -48,19 +51,56 @@ function updateUIForLoggedIn(email) {
|
|
| 48 |
accountMenu.classList.remove('hidden');
|
| 49 |
accountMenu.classList.add('hidden');
|
| 50 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
updateTranslations();
|
| 52 |
}
|
| 53 |
|
| 54 |
function updateUIForLoggedOut() {
|
| 55 |
const signInBtn = document.getElementById('sign-in-btn');
|
| 56 |
const accountMenu = document.getElementById('account-menu');
|
|
|
|
|
|
|
|
|
|
| 57 |
if (signInBtn) {
|
| 58 |
signInBtn.textContent = 'Sign In';
|
| 59 |
-
signInBtn.onclick = (event) =>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
}
|
| 61 |
if (accountMenu) {
|
| 62 |
accountMenu.classList.add('hidden');
|
| 63 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
updateTranslations();
|
| 65 |
}
|
| 66 |
|
|
@@ -68,7 +108,9 @@ async function handleLogout() {
|
|
| 68 |
const result = await apiCall('/api/logout', { method: 'POST' });
|
| 69 |
if (result.success) {
|
| 70 |
updateUIForLoggedOut();
|
| 71 |
-
|
|
|
|
|
|
|
| 72 |
} else {
|
| 73 |
alert(result.data?.message || (getCurrentLanguage() === 'az' ? 'Sistemdən çıxışda xəta.' : 'Error logging out.'));
|
| 74 |
}
|
|
@@ -90,7 +132,9 @@ async function handleLogin(event) {
|
|
| 90 |
successDiv.classList.add('bg-green-100', 'text-green-700');
|
| 91 |
updateUIForLoggedIn(email);
|
| 92 |
setTimeout(() => {
|
| 93 |
-
|
|
|
|
|
|
|
| 94 |
successDiv.classList.add('hidden');
|
| 95 |
document.getElementById('login-form').reset();
|
| 96 |
}, 1500);
|
|
|
|
| 37 |
function updateUIForLoggedIn(email) {
|
| 38 |
const signInBtn = document.getElementById('sign-in-btn');
|
| 39 |
const accountMenu = document.getElementById('account-menu');
|
| 40 |
+
const mobileSignInBtn = document.getElementById('mobile-sign-in-btn');
|
| 41 |
+
const mobileAccountMenu = document.getElementById('mobile-account-menu');
|
| 42 |
+
|
| 43 |
if (signInBtn) {
|
| 44 |
signInBtn.textContent = email || 'Dashboard';
|
| 45 |
signInBtn.onclick = (event) => {
|
|
|
|
| 51 |
accountMenu.classList.remove('hidden');
|
| 52 |
accountMenu.classList.add('hidden');
|
| 53 |
}
|
| 54 |
+
|
| 55 |
+
if (mobileSignInBtn) {
|
| 56 |
+
mobileSignInBtn.textContent = email || 'Dashboard';
|
| 57 |
+
mobileSignInBtn.onclick = (event) => {
|
| 58 |
+
event.preventDefault && event.preventDefault();
|
| 59 |
+
if (mobileAccountMenu) {
|
| 60 |
+
mobileAccountMenu.classList.toggle('hidden');
|
| 61 |
+
}
|
| 62 |
+
};
|
| 63 |
+
}
|
| 64 |
+
if (mobileAccountMenu) {
|
| 65 |
+
mobileAccountMenu.classList.add('hidden');
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
updateTranslations();
|
| 69 |
}
|
| 70 |
|
| 71 |
function updateUIForLoggedOut() {
|
| 72 |
const signInBtn = document.getElementById('sign-in-btn');
|
| 73 |
const accountMenu = document.getElementById('account-menu');
|
| 74 |
+
const mobileSignInBtn = document.getElementById('mobile-sign-in-btn');
|
| 75 |
+
const mobileAccountMenu = document.getElementById('mobile-account-menu');
|
| 76 |
+
|
| 77 |
if (signInBtn) {
|
| 78 |
signInBtn.textContent = 'Sign In';
|
| 79 |
+
signInBtn.onclick = (event) => {
|
| 80 |
+
if (typeof window.navigateTo === 'function') {
|
| 81 |
+
window.navigateTo('login', event);
|
| 82 |
+
}
|
| 83 |
+
};
|
| 84 |
}
|
| 85 |
if (accountMenu) {
|
| 86 |
accountMenu.classList.add('hidden');
|
| 87 |
}
|
| 88 |
+
|
| 89 |
+
if (mobileSignInBtn) {
|
| 90 |
+
mobileSignInBtn.textContent = 'Sign In';
|
| 91 |
+
mobileSignInBtn.onclick = (event) => {
|
| 92 |
+
if (typeof window.navigateTo === 'function') {
|
| 93 |
+
window.navigateTo('login', event);
|
| 94 |
+
if (typeof window.closeMobileMenu === 'function') {
|
| 95 |
+
window.closeMobileMenu();
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
+
};
|
| 99 |
+
}
|
| 100 |
+
if (mobileAccountMenu) {
|
| 101 |
+
mobileAccountMenu.classList.add('hidden');
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
updateTranslations();
|
| 105 |
}
|
| 106 |
|
|
|
|
| 108 |
const result = await apiCall('/api/logout', { method: 'POST' });
|
| 109 |
if (result.success) {
|
| 110 |
updateUIForLoggedOut();
|
| 111 |
+
if (typeof window.navigateTo === 'function') {
|
| 112 |
+
window.navigateTo('home', { preventDefault: () => {} });
|
| 113 |
+
}
|
| 114 |
} else {
|
| 115 |
alert(result.data?.message || (getCurrentLanguage() === 'az' ? 'Sistemdən çıxışda xəta.' : 'Error logging out.'));
|
| 116 |
}
|
|
|
|
| 132 |
successDiv.classList.add('bg-green-100', 'text-green-700');
|
| 133 |
updateUIForLoggedIn(email);
|
| 134 |
setTimeout(() => {
|
| 135 |
+
if (typeof window.navigateTo === 'function') {
|
| 136 |
+
window.navigateTo('dashboard', event);
|
| 137 |
+
}
|
| 138 |
successDiv.classList.add('hidden');
|
| 139 |
document.getElementById('login-form').reset();
|
| 140 |
}, 1500);
|
static/js/dashboard.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
| 1 |
import { apiCall } from './auth.js';
|
| 2 |
import { updateDarkModeGradients } from './darkmode.js';
|
|
|
|
| 3 |
|
| 4 |
async function loadDashboard() {
|
| 5 |
const authResult = await apiCall('/api/check-auth');
|
| 6 |
if (!authResult.success || !authResult.data.authenticated) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
return;
|
| 8 |
}
|
| 9 |
const imagesResult = await apiCall('/api/my-images');
|
|
|
|
| 1 |
import { apiCall } from './auth.js';
|
| 2 |
import { updateDarkModeGradients } from './darkmode.js';
|
| 3 |
+
import { getCurrentLanguage } from './i18n.js';
|
| 4 |
|
| 5 |
async function loadDashboard() {
|
| 6 |
const authResult = await apiCall('/api/check-auth');
|
| 7 |
if (!authResult.success || !authResult.data.authenticated) {
|
| 8 |
+
alert(getCurrentLanguage() === 'az' ? 'Zəhmət olmasa əvvəlcə daxil olun' : 'Please sign in first to access the dashboard');
|
| 9 |
+
// Use global navigateTo function (defined in main.js)
|
| 10 |
+
if (typeof window.navigateTo === 'function') {
|
| 11 |
+
window.navigateTo('login', { preventDefault: () => {} });
|
| 12 |
+
}
|
| 13 |
return;
|
| 14 |
}
|
| 15 |
const imagesResult = await apiCall('/api/my-images');
|
static/js/main.js
CHANGED
|
@@ -31,17 +31,10 @@ initMenuEvents();
|
|
| 31 |
initUpload();
|
| 32 |
if (window.location.hash) {
|
| 33 |
const page = window.location.hash.substring(1);
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
const index = pages.indexOf(page);
|
| 40 |
-
if (index >= 0) {
|
| 41 |
-
document.querySelectorAll('.sidebar-dot').forEach(dot => dot.classList.remove('active'));
|
| 42 |
-
const dots = document.querySelectorAll('.sidebar-dot');
|
| 43 |
-
if (dots[index]) dots[index].classList.add('active');
|
| 44 |
-
}
|
| 45 |
-
}
|
| 46 |
}
|
| 47 |
initElementSdk();
|
|
|
|
| 31 |
initUpload();
|
| 32 |
if (window.location.hash) {
|
| 33 |
const page = window.location.hash.substring(1);
|
| 34 |
+
// Use navigateTo to ensure authentication checks are performed
|
| 35 |
+
navigateTo(page, { preventDefault: () => {} });
|
| 36 |
+
} else {
|
| 37 |
+
// If no hash, ensure home page is shown
|
| 38 |
+
navigateTo('home', { preventDefault: () => {} });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
}
|
| 40 |
initElementSdk();
|
static/js/menu.js
CHANGED
|
@@ -59,6 +59,11 @@ function initMenuEvents() {
|
|
| 59 |
if (accountBtn && accountMenu && !accountBtn.contains(e.target) && !accountMenu.contains(e.target)) {
|
| 60 |
accountMenu.classList.add('hidden');
|
| 61 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
});
|
| 63 |
}
|
| 64 |
|
|
|
|
| 59 |
if (accountBtn && accountMenu && !accountBtn.contains(e.target) && !accountMenu.contains(e.target)) {
|
| 60 |
accountMenu.classList.add('hidden');
|
| 61 |
}
|
| 62 |
+
const mobileAccountBtn = document.getElementById('mobile-sign-in-btn');
|
| 63 |
+
const mobileAccountMenu = document.getElementById('mobile-account-menu');
|
| 64 |
+
if (mobileAccountBtn && mobileAccountMenu && !mobileAccountBtn.contains(e.target) && !mobileAccountMenu.contains(e.target)) {
|
| 65 |
+
mobileAccountMenu.classList.add('hidden');
|
| 66 |
+
}
|
| 67 |
});
|
| 68 |
}
|
| 69 |
|
static/js/navigation.js
CHANGED
|
@@ -1,9 +1,22 @@
|
|
| 1 |
import { loadDashboard } from './dashboard.js';
|
|
|
|
|
|
|
| 2 |
|
| 3 |
-
function navigateTo(page, event) {
|
| 4 |
if (event && event.preventDefault) {
|
| 5 |
event.preventDefault();
|
| 6 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
|
| 8 |
const pageElement = document.getElementById(`${page}-page`);
|
| 9 |
if (pageElement) {
|
|
|
|
| 1 |
import { loadDashboard } from './dashboard.js';
|
| 2 |
+
import { apiCall } from './auth.js';
|
| 3 |
+
import { getCurrentLanguage } from './i18n.js';
|
| 4 |
|
| 5 |
+
async function navigateTo(page, event) {
|
| 6 |
if (event && event.preventDefault) {
|
| 7 |
event.preventDefault();
|
| 8 |
}
|
| 9 |
+
|
| 10 |
+
// Check authentication for protected pages
|
| 11 |
+
const protectedPages = ['analyze', 'dashboard'];
|
| 12 |
+
if (protectedPages.includes(page)) {
|
| 13 |
+
const authResult = await apiCall('/api/check-auth');
|
| 14 |
+
if (!authResult.success || !authResult.data.authenticated) {
|
| 15 |
+
alert(getCurrentLanguage() === 'az' ? 'Zəhmət olmasa əvvəlcə daxil olun' : 'Please sign in first to access this page');
|
| 16 |
+
page = 'login';
|
| 17 |
+
}
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
|
| 21 |
const pageElement = document.getElementById(`${page}-page`);
|
| 22 |
if (pageElement) {
|
templates/base.html
CHANGED
|
@@ -1,19 +1,19 @@
|
|
| 1 |
-
<!doctype html>
|
| 2 |
-
<html lang="en">
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="UTF-8">
|
| 5 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
-
<title>Agrovision - Future of Farming</title>
|
| 7 |
-
<link rel="stylesheet" href="/static/styles.css">
|
| 8 |
-
<script src="/_sdk/element_sdk.js"></script>
|
| 9 |
-
<script src="https://cdn.tailwindcss.com"></script>
|
| 10 |
-
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.3/build/qrcode.min.js"></script>
|
| 11 |
-
</head>
|
| 12 |
-
<body class="bg-gray-50">
|
| 13 |
-
{% include 'components/dark_toggle.html' %}
|
| 14 |
-
{% include 'components/header.html' %}
|
| 15 |
-
{% block content %}{% endblock %}
|
| 16 |
-
<script src="/static/translations.js"></script>
|
| 17 |
-
<script type="module" src="/static/js/main.js"></script>
|
| 18 |
-
</body>
|
| 19 |
-
</html>
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Agrovision - Future of Farming</title>
|
| 7 |
+
<link rel="stylesheet" href="/static/styles.css">
|
| 8 |
+
<script src="/_sdk/element_sdk.js"></script>
|
| 9 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 10 |
+
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.3/build/qrcode.min.js"></script>
|
| 11 |
+
</head>
|
| 12 |
+
<body class="bg-gray-50">
|
| 13 |
+
{% include 'components/dark_toggle.html' %}
|
| 14 |
+
{% include 'components/header.html' %}
|
| 15 |
+
{% block content %}{% endblock %}
|
| 16 |
+
<script src="/static/translations.js"></script>
|
| 17 |
+
<script type="module" src="/static/js/main.js"></script>
|
| 18 |
+
</body>
|
| 19 |
+
</html>
|
templates/components/header.html
CHANGED
|
@@ -1,9 +1,9 @@
|
|
| 1 |
<header class="fixed top-0 left-0 right-0 bg-white/95 backdrop-blur-sm shadow-sm z-50">
|
| 2 |
<nav class="max-w-7xl mx-auto px-4 md:px-6 py-4">
|
| 3 |
<div class="flex justify-between items-center">
|
| 4 |
-
<
|
| 5 |
<div class="w-10 h-10 bg-gradient-to-br from-green-500 to-orange-500 rounded-lg flex items-center justify-center text-white text-xl font-bold">A</div><span id="nav-title" class="text-xl md:text-2xl font-bold gradient-text">Agrovision</span>
|
| 6 |
-
</
|
| 7 |
<div class="hidden md:flex items-center space-x-8"><a href="#home" class="nav-item text-gray-700 font-medium" data-i18n="nav.home" onclick="navigateTo('home', event)">Home</a> <a href="#features" class="nav-item text-gray-700 font-medium" data-i18n="nav.features" onclick="navigateTo('features', event)">Features</a> <a href="#analyze" class="nav-item text-gray-700 font-medium" data-i18n="nav.analyze" onclick="navigateTo('analyze', event)">Analyze</a> <a href="#dashboard" class="nav-item text-gray-700 font-medium" data-i18n="nav.dashboard" onclick="navigateTo('dashboard', event)">Dashboard</a> <a href="#gallery" class="nav-item text-gray-700 font-medium" data-i18n="nav.gallery" onclick="navigateTo('gallery', event)">Gallery</a> <a href="#contact" class="nav-item text-gray-700 font-medium" data-i18n="nav.contact" onclick="navigateTo('contact', event)">Contact</a></div>
|
| 8 |
<div class="hidden md:flex items-center space-x-4">
|
| 9 |
<div class="relative">
|
|
@@ -26,26 +26,6 @@
|
|
| 26 |
<svg id="close-icon" class="w-6 h-6 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
|
| 27 |
</button>
|
| 28 |
</div>
|
| 29 |
-
|
| 30 |
-
<div class="px-4 space-y-2">
|
| 31 |
-
<a href="#home" class="block px-4 py-3 rounded-lg text-gray-700 font-medium hover:bg-gray-100 transition" data-i18n="nav.home" onclick="navigateTo('home', event); closeMobileMenu();">Home</a>
|
| 32 |
-
<a href="#features" class="block px-4 py-3 rounded-lg text-gray-700 font-medium hover:bg-gray-100 transition" data-i18n="nav.features" onclick="navigateTo('features', event); closeMobileMenu();">Features</a>
|
| 33 |
-
<a href="#analyze" class="block px-4 py-3 rounded-lg text-gray-700 font-medium hover:bg-gray-100 transition" data-i18n="nav.analyze" onclick="navigateTo('analyze', event); closeMobileMenu();">Analyze</a>
|
| 34 |
-
<a href="#dashboard" class="block px-4 py-3 rounded-lg text-gray-700 font-medium hover:bg-gray-100 transition" data-i18n="nav.dashboard" onclick="navigateTo('dashboard', event); closeMobileMenu();">Dashboard</a>
|
| 35 |
-
<a href="#gallery" class="block px-4 py-3 rounded-lg text-gray-700 font-medium hover:bg-gray-100 transition" data-i18n="nav.gallery" onclick="navigateTo('gallery', event); closeMobileMenu();">Gallery</a>
|
| 36 |
-
<a href="#contact" class="block px-4 py-3 rounded-lg text-gray-700 font-medium hover:bg-gray-100 transition" data-i18n="nav.contact" onclick="navigateTo('contact', event); closeMobileMenu();">Contact</a>
|
| 37 |
-
<div class="border-t border-gray-200 my-2"></div>
|
| 38 |
-
<div class="px-4 py-2">
|
| 39 |
-
<div class="relative mb-3">
|
| 40 |
-
<button id="mobile-lang-switcher" class="w-full bg-gray-100 hover:bg-gray-200 text-gray-700 px-4 py-2 rounded-lg font-semibold transition flex items-center justify-between" onclick="toggleMobileLanguageMenu()"><span>Language</span><span id="mobile-current-lang">EN</span></button>
|
| 41 |
-
<div id="mobile-lang-menu" class="hidden absolute top-full left-0 right-0 mt-2 bg-white rounded-lg shadow-lg border border-gray-200 py-2 z-50">
|
| 42 |
-
<button class="w-full text-left px-4 py-2 hover:bg-gray-100 text-gray-700 font-medium" onclick="switchLanguage('en'); closeMobileMenu();">🇬🇧 English</button>
|
| 43 |
-
<button class="w-full text-left px-4 py-2 hover:bg-gray-100 text-gray-700 font-medium" onclick="switchLanguage('az'); closeMobileMenu();">🇦🇿 Azərbaycanca</button>
|
| 44 |
-
</div>
|
| 45 |
-
</div>
|
| 46 |
-
<button class="w-full btn-gradient-orange text-white px-6 py-3 rounded-lg font-semibold" onclick="navigateTo('login', event); closeMobileMenu();"> Sign In </button>
|
| 47 |
-
</div>
|
| 48 |
-
</div>
|
| 49 |
-
</div>
|
| 50 |
</nav>
|
| 51 |
</header>
|
|
|
|
| 1 |
<header class="fixed top-0 left-0 right-0 bg-white/95 backdrop-blur-sm shadow-sm z-50">
|
| 2 |
<nav class="max-w-7xl mx-auto px-4 md:px-6 py-4">
|
| 3 |
<div class="flex justify-between items-center">
|
| 4 |
+
<a href="#home" class="flex items-center space-x-2 cursor-pointer hover:opacity-80 transition-opacity" onclick="navigateTo('home', event)">
|
| 5 |
<div class="w-10 h-10 bg-gradient-to-br from-green-500 to-orange-500 rounded-lg flex items-center justify-center text-white text-xl font-bold">A</div><span id="nav-title" class="text-xl md:text-2xl font-bold gradient-text">Agrovision</span>
|
| 6 |
+
</a>
|
| 7 |
<div class="hidden md:flex items-center space-x-8"><a href="#home" class="nav-item text-gray-700 font-medium" data-i18n="nav.home" onclick="navigateTo('home', event)">Home</a> <a href="#features" class="nav-item text-gray-700 font-medium" data-i18n="nav.features" onclick="navigateTo('features', event)">Features</a> <a href="#analyze" class="nav-item text-gray-700 font-medium" data-i18n="nav.analyze" onclick="navigateTo('analyze', event)">Analyze</a> <a href="#dashboard" class="nav-item text-gray-700 font-medium" data-i18n="nav.dashboard" onclick="navigateTo('dashboard', event)">Dashboard</a> <a href="#gallery" class="nav-item text-gray-700 font-medium" data-i18n="nav.gallery" onclick="navigateTo('gallery', event)">Gallery</a> <a href="#contact" class="nav-item text-gray-700 font-medium" data-i18n="nav.contact" onclick="navigateTo('contact', event)">Contact</a></div>
|
| 8 |
<div class="hidden md:flex items-center space-x-4">
|
| 9 |
<div class="relative">
|
|
|
|
| 26 |
<svg id="close-icon" class="w-6 h-6 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
|
| 27 |
</button>
|
| 28 |
</div>
|
| 29 |
+
{% include 'components/mobile_menu.html' %}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
</nav>
|
| 31 |
</header>
|
templates/components/mobile_menu.html
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<div id="mobile-menu" class="hidden md:hidden absolute top-full left-0 right-0 bg-white shadow-lg border-t border-gray-200 py-4 z-40">
|
| 2 |
+
<div class="px-4 space-y-2">
|
| 3 |
+
<a href="#home" class="block px-4 py-3 rounded-lg text-gray-700 font-medium hover:bg-gray-100 transition" data-i18n="nav.home" onclick="navigateTo('home', event); closeMobileMenu();">Home</a>
|
| 4 |
+
<a href="#features" class="block px-4 py-3 rounded-lg text-gray-700 font-medium hover:bg-gray-100 transition" data-i18n="nav.features" onclick="navigateTo('features', event); closeMobileMenu();">Features</a>
|
| 5 |
+
<a href="#analyze" class="block px-4 py-3 rounded-lg text-gray-700 font-medium hover:bg-gray-100 transition" data-i18n="nav.analyze" onclick="navigateTo('analyze', event); closeMobileMenu();">Analyze</a>
|
| 6 |
+
<a href="#dashboard" class="block px-4 py-3 rounded-lg text-gray-700 font-medium hover:bg-gray-100 transition" data-i18n="nav.dashboard" onclick="navigateTo('dashboard', event); closeMobileMenu();">Dashboard</a>
|
| 7 |
+
<a href="#gallery" class="block px-4 py-3 rounded-lg text-gray-700 font-medium hover:bg-gray-100 transition" data-i18n="nav.gallery" onclick="navigateTo('gallery', event); closeMobileMenu();">Gallery</a>
|
| 8 |
+
<a href="#contact" class="block px-4 py-3 rounded-lg text-gray-700 font-medium hover:bg-gray-100 transition" data-i18n="nav.contact" onclick="navigateTo('contact', event); closeMobileMenu();">Contact</a>
|
| 9 |
+
<div class="border-t border-gray-200 my-2"></div>
|
| 10 |
+
<div class="px-4 py-2">
|
| 11 |
+
<div class="relative mb-3">
|
| 12 |
+
<button id="mobile-lang-switcher" class="w-full bg-gray-100 hover:bg-gray-200 text-gray-700 px-4 py-2 rounded-lg font-semibold transition flex items-center justify-between" onclick="toggleMobileLanguageMenu()"><span>Language</span><span id="mobile-current-lang">EN</span></button>
|
| 13 |
+
<div id="mobile-lang-menu" class="hidden absolute top-full left-0 right-0 mt-2 bg-white rounded-lg shadow-lg border border-gray-200 py-2 z-50">
|
| 14 |
+
<button class="w-full text-left px-4 py-2 hover:bg-gray-100 text-gray-700 font-medium" onclick="switchLanguage('en'); closeMobileMenu();">🇬🇧 English</button>
|
| 15 |
+
<button class="w-full text-left px-4 py-2 hover:bg-gray-100 text-gray-700 font-medium" onclick="switchLanguage('az'); closeMobileMenu();">🇦🇿 Azərbaycanca</button>
|
| 16 |
+
</div>
|
| 17 |
+
</div>
|
| 18 |
+
<button id="mobile-sign-in-btn" class="w-full btn-gradient-orange text-white px-6 py-3 rounded-lg font-semibold" onclick="navigateTo('login', event); closeMobileMenu();"> Sign In </button>
|
| 19 |
+
<div id="mobile-account-menu" class="hidden mt-2 space-y-2">
|
| 20 |
+
<button class="w-full text-left px-4 py-3 rounded-lg text-gray-700 font-medium hover:bg-gray-100 transition" data-i18n="nav.dashboard" onclick="navigateTo('dashboard', event); closeMobileMenu();">Dashboard</button>
|
| 21 |
+
<button class="w-full text-left px-4 py-3 rounded-lg text-red-600 font-medium hover:bg-red-50 transition" data-i18n="nav.signout" onclick="handleLogout(); closeMobileMenu();">Sign out</button>
|
| 22 |
+
</div>
|
| 23 |
+
</div>
|
| 24 |
+
</div>
|
| 25 |
+
</div>
|
| 26 |
+
|