Funybubble commited on
Commit
c7257f7
·
verified ·
1 Parent(s): e61132e

Upload 30 files

Browse files
DISABLE_RATE_LIMIT.txt ADDED
File without changes
README.md CHANGED
@@ -1,10 +1,27 @@
1
- ---
2
- title: Aaaaa1
3
- emoji: 👁
4
- colorFrom: blue
5
- colorTo: purple
6
- sdk: static
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Aa21123a
3
+ emoji: 🐨
4
+ colorFrom: red
5
+ colorTo: gray
6
+ sdk: static
7
+ pinned: false
8
+ ---
9
+
10
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
11
+ "# cbc"
12
+
13
+ # Čebelarstvo Cigoj Admin Panel
14
+
15
+ ## Admin Usage
16
+ - Login: `/admin_login.php` (username: `admin`, password: your set password)
17
+ - Dashboard: `/backend.php` (requires login)
18
+ - Logout: `/logout.php` (clears session)
19
+
20
+ ## Troubleshooting
21
+ - If you get redirected to login repeatedly, check your database for the correct admin user and role.
22
+ - If preview/iframe blocks navigation, use the "Open dashboard in new tab" button after login.
23
+
24
+ ## Security
25
+ - Remove `DISABLE_RATE_LIMIT` for production.
26
+ - Set cookies to `secure` and `SameSite=None` for HTTPS deployments.
27
+ - Change the hardcoded JWT secret in `config.php` before going live.
Unconfirmed 642953.crdownload ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Cart Fix Script - Connects the "V košarico" buttons to cart functionality
2
+ document.addEventListener('DOMContentLoaded', function(){
3
+ function parsePrice(text){
4
+ const m = text && text.match(/([\d.,]+)\s*€/);
5
+ return m ? parseFloat(m[1].replace(/\./g,'').replace(',', '.')) : 0;
6
+ }
7
+
8
+ // Do NOT generate fake product IDs. Use the real data-product-id from backend-rendered HTML.
9
+
10
+ // Fallback mapping: product name -> actual DB id
11
+ const PRODUCT_NAME_TO_ID = {
12
+ 'Cvetni prah 50g': '1',
13
+ 'Cvetni prah 100g': '1',
14
+ 'Cvetni prah': '1',
15
+ 'Balzam za ustnice iz čebeljega voska': '2',
16
+ 'Balzam za ustnice': '2',
17
+ 'Balzam': '2',
18
+ 'Med ajdov': '3',
19
+ 'Med': '3',
20
+ 'Med ajdov ': '3'
21
+ };
22
+
23
+ document.addEventListener('click', function(e){
24
+ const btn = e.target.closest('button');
25
+ if (!btn || !/V košarico/i.test(btn.textContent)) return;
26
+ e.preventDefault();
27
+
28
+ // Find the exact product container this button belongs to
29
+ const card = btn.closest('.product-card, .group');
30
+ if (!card) {
31
+ console.log('No product card found');
32
+ return;
33
+ }
34
+
35
+ const nameEl = card.querySelector('h3');
36
+ const priceEl = card.querySelector('p[class*="amber-700"], p[class*="font-bold"]') ||
37
+ Array.from(card.querySelectorAll('p')).find(p => /€/.test(p.textContent));
38
+
39
+ const name = nameEl ? nameEl.textContent.trim() : 'Izdelek';
40
+ const price = parsePrice(priceEl ? priceEl.textContent : '0');
41
+
42
+ // Use the real productId from the HTML attribute; if missing, fall back to name->id map
43
+ let id = card.getAttribute('data-product-id');
44
+ if (!id) {
45
+ id = PRODUCT_NAME_TO_ID[name] || null;
46
+ }
47
+
48
+ if (!id) {
49
+ console.warn('No product id found for item, skipping add to cart', name);
50
+ return;
51
+ }
52
+
53
+ console.log('Adding to cart:', {id, name, price});
54
+ addToCart(id, name, price);
55
+ updateCartCount();
56
+
57
+ // Visual feedback
58
+ const originalHTML = btn.innerHTML;
59
+ btn.innerHTML = '<i data-feather="check" class="mr-1 w-4 h-4"></i> Dodano!';
60
+ btn.style.backgroundColor = '#10b981';
61
+ btn.disabled = true;
62
+
63
+ setTimeout(() => {
64
+ btn.innerHTML = originalHTML;
65
+ btn.style.backgroundColor = '';
66
+ btn.disabled = false;
67
+ feather.replace();
68
+ }, 2000);
69
+ });
70
+ });
admin_login.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Simple admin login page that posts to login.php
3
+ ?>
4
+ <!DOCTYPE html>
5
+ <html lang="sl">
6
+ <head>
7
+ <meta charset="utf-8">
8
+ <meta name="viewport" content="width=device-width, initial-scale=1">
9
+ <title>Admin prijava | Čebelarstvo Cigoj</title>
10
+ <script src="https://cdn.tailwindcss.com"></script>
11
+ </head>
12
+ <body class="bg-amber-50 min-h-screen flex items-center justify-center">
13
+ <div class="max-w-md w-full bg-white rounded-lg shadow p-6">
14
+ <h1 class="text-2xl font-bold mb-4 text-amber-900">Administracija - Prijava</h1>
15
+ <!-- target="_top" attempts to replace the top-level browsing context when inside an iframe/preview -->
16
+ <form id="loginForm" class="space-y-4" method="post" action="login.php" target="_top">
17
+ <div>
18
+ <label class="block text-sm font-medium text-gray-700">Uporabniško ime</label>
19
+ <input id="username" name="username" type="text" required class="mt-1 block w-full rounded border-gray-300 shadow-sm focus:ring-amber-500 focus:border-amber-500" />
20
+ </div>
21
+ <div>
22
+ <label class="block text-sm font-medium text-gray-700">Geslo</label>
23
+ <input id="password" name="password" type="password" required class="mt-1 block w-full rounded border-gray-300 shadow-sm focus:ring-amber-500 focus:border-amber-500" />
24
+ </div>
25
+ <div>
26
+ <button type="submit" class="w-full inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-amber-700 hover:bg-amber-800">Prijava</button>
27
+ </div>
28
+ <div id="error" class="text-red-600 text-sm"></div>
29
+ </form>
30
+ <div class="mt-4">
31
+ <label class="block text-sm font-medium text-gray-700">Ročno prilepi žeton (če preview blokira piškotke)</label>
32
+ <div class="flex mt-2">
33
+ <input id="pasteToken" class="flex-1 rounded border-gray-300 shadow-sm p-2" placeholder="Prilepite JWT sem..." />
34
+ <button id="applyToken" class="ml-2 bg-amber-600 text-white px-3 py-2 rounded">Uporabi</button>
35
+ </div>
36
+ </div>
37
+ <div class="mt-4 text-sm text-gray-600">
38
+ <p>If the preview still blocks navigation, you can open the dashboard manually:</p>
39
+ <div class="mt-2">
40
+ <a href="backend.php" target="_blank" class="inline-block bg-amber-100 border border-amber-300 text-amber-700 px-3 py-2 rounded">Open dashboard in new tab</a>
41
+ </div>
42
+ </div>
43
+ </div>
44
+
45
+ <script>
46
+ // Minimal JS: keep paste-token helper and top-window redirect attempt for special previews.
47
+ document.getElementById('applyToken').addEventListener('click', function(e) {
48
+ const token = document.getElementById('pasteToken').value.trim();
49
+ if (!token) return;
50
+ const expires = new Date(Date.now() + 7*24*60*60*1000).toUTCString();
51
+ try {
52
+ document.cookie = `refresh_token=${token}; expires=${expires}; path=/; SameSite=None; Secure`;
53
+ } catch (e) {
54
+ // ignore
55
+ }
56
+ try {
57
+ if (window.top && window.top !== window) {
58
+ window.top.location.replace('backend.php');
59
+ return;
60
+ }
61
+ } catch (e) {}
62
+ window.location.replace('backend.php');
63
+ });
64
+ </script>
65
+ </body>
66
+ </html>
admin_orders.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once 'require_auth.php';
3
+ // Fetch orders and items
4
+ try {
5
+ $stmt = $pdo->prepare("SELECT * FROM orders ORDER BY created_at DESC");
6
+ $stmt->execute();
7
+ $orders = $stmt->fetchAll(PDO::FETCH_ASSOC);
8
+
9
+ foreach ($orders as &$order) {
10
+ $stmt = $pdo->prepare("SELECT * FROM order_items WHERE order_id = ?");
11
+ $stmt->execute([$order['id']]);
12
+ $order['items'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
13
+ }
14
+ } catch (Exception $e) {
15
+ $orders = [];
16
+ }
17
+ ?>
18
+ <!DOCTYPE html>
19
+ <html lang="sl">
20
+ <head>
21
+ <meta charset="UTF-8">
22
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
23
+ <title>Admin - Naročila</title>
24
+ <script src="https://cdn.tailwindcss.com"></script>
25
+ </head>
26
+ <body class="bg-gray-100 font-sans">
27
+ <div class="max-w-6xl mx-auto p-6">
28
+ <h1 class="text-2xl font-bold mb-4">Vsa naročila</h1>
29
+
30
+ <?php if (empty($orders)): ?>
31
+ <p class="text-gray-600">Ni naročil za prikaz.</p>
32
+ <?php else: ?>
33
+ <?php foreach ($orders as $order): ?>
34
+ <div class="bg-white shadow rounded mb-4 p-4">
35
+ <div class="flex justify-between items-start">
36
+ <div>
37
+ <h2 class="text-lg font-semibold">Naročilo #<?php echo htmlspecialchars($order['id']); ?></h2>
38
+ <p class="text-sm text-gray-600"><?php echo htmlspecialchars($order['customer_name']); ?> — <?php echo htmlspecialchars($order['customer_email']); ?></p>
39
+ <p class="text-sm text-gray-600">Status: <?php echo htmlspecialchars($order['status']); ?></p>
40
+ </div>
41
+ <div class="text-right">
42
+ <p class="text-sm text-gray-600"><?php echo htmlspecialchars($order['created_at']); ?></p>
43
+ <p class="text-lg font-bold"><?php echo htmlspecialchars(number_format((float)$order['total_amount'], 2, ',', '.')) . '€'; ?></p>
44
+ </div>
45
+ </div>
46
+
47
+ <?php if (!empty($order['items'])): ?>
48
+ <div class="mt-3">
49
+ <table class="w-full text-left text-sm">
50
+ <thead class="text-xs text-gray-500 uppercase">
51
+ <tr>
52
+ <th>Izdelek</th>
53
+ <th class="text-right">Količina</th>
54
+ <th class="text-right">Enotna cena</th>
55
+ <th class="text-right">Skupaj</th>
56
+ </tr>
57
+ </thead>
58
+ <tbody>
59
+ <?php foreach ($order['items'] as $item): ?>
60
+ <tr>
61
+ <td><?php echo htmlspecialchars($item['product_name'] ?? $item['name'] ?? 'Izdelek'); ?></td>
62
+ <td class="text-right"><?php echo htmlspecialchars($item['quantity']); ?></td>
63
+ <td class="text-right"><?php echo htmlspecialchars(number_format((float)($item['unit_price'] ?? $item['price'] ?? 0), 2, ',', '.')) . '€'; ?></td>
64
+ <td class="text-right"><?php echo htmlspecialchars(number_format((float)($item['unit_price'] * $item['quantity']), 2, ',', '.')) . '€'; ?></td>
65
+ </tr>
66
+ <?php endforeach; ?>
67
+ </tbody>
68
+ </table>
69
+ </div>
70
+ <?php endif; ?>
71
+ </div>
72
+ <?php endforeach; ?>
73
+ <?php endif; ?>
74
+ </div>
75
+ </body>
76
+ </html>
auth.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once 'config.php';
3
+
4
+ // Handle admin login
5
+ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
6
+ $data = json_decode(file_get_contents("php://input"));
7
+
8
+ // Validate admin credentials
9
+ if($data->username === 'admin' && $data->password === 'admin123') {
10
+ $token = array(
11
+ "iss" => "cebelarstvo_cigoj",
12
+ "iat" => time(),
13
+ "exp" => time() + (60 * 60),
14
+ "data" => array(
15
+ "username" => "admin",
16
+ "role" => "admin"
17
+ )
18
+ );
19
+
20
+ $jwt = \Firebase\JWT\JWT::encode($token, JWT_SECRET, 'HS256');
21
+ echo json_encode(array("success" => true, "token" => $jwt));
22
+ } else {
23
+ http_response_code(401);
24
+ echo json_encode(array("success" => false, "message" => "Invalid credentials"));
25
+ }
26
+ }
27
+ ?>
backend.php ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once 'require_auth.php';
3
+
4
+ // Fetch basic stats and recent orders for the dashboard
5
+ try {
6
+ // Orders count and revenue
7
+ $stmt = $pdo->query("SELECT COUNT(*) as orders_count, COALESCE(SUM(total_amount),0) as revenue FROM orders");
8
+ $stats = $stmt->fetch(PDO::FETCH_ASSOC);
9
+ $orders_count = $stats['orders_count'] ?? 0;
10
+ $revenue = $stats['revenue'] ?? 0.00;
11
+
12
+ // Products count
13
+ $stmt = $pdo->query("SELECT COUNT(*) FROM products");
14
+ $products_count = (int)$stmt->fetchColumn();
15
+
16
+ // Customers count
17
+ $stmt = $pdo->query("SELECT COUNT(*) FROM customers");
18
+ $customers_count = (int)$stmt->fetchColumn();
19
+
20
+ // Recent orders (latest 10)
21
+ $stmt = $pdo->prepare("SELECT id, customer_name, status, total_amount, created_at FROM orders ORDER BY created_at DESC LIMIT 10");
22
+ $stmt->execute();
23
+ $recentOrders = $stmt->fetchAll(PDO::FETCH_ASSOC);
24
+ } catch (Exception $e) {
25
+ // Graceful fallback to zeros/empty if the DB query fails
26
+ $orders_count = $orders_count ?? 0;
27
+ $revenue = $revenue ?? 0.00;
28
+ $products_count = $products_count ?? 0;
29
+ $customers_count = $customers_count ?? 0;
30
+ $recentOrders = [];
31
+ }
32
+ ?>
33
+ <!DOCTYPE html>
34
+ <html lang="sl">
35
+ <head>
36
+ <meta charset="UTF-8">
37
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
38
+ <title>Administracija | Čebelarstvo Cigoj</title>
39
+ <script src="https://cdn.tailwindcss.com"></script>
40
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
41
+ <style>
42
+ .sidebar {
43
+ transition: all 0.3s;
44
+ }
45
+ .sidebar-link:hover {
46
+ background-color: rgba(251, 191, 36, 0.1);
47
+ }
48
+ .dashboard-card {
49
+ transition: transform 0.2s;
50
+ }
51
+ .dashboard-card:hover {
52
+ transform: translateY(-2px);
53
+ }
54
+ </style>
55
+ </head>
56
+ <body class="bg-gray-100 font-sans">
57
+ <!-- Admin Layout -->
58
+ <div class="flex h-screen">
59
+ <!-- Sidebar -->
60
+ <div class="sidebar bg-amber-800 text-white w-64 flex-shrink-0">
61
+ <div class="p-4 border-b border-amber-700">
62
+ <h1 class="text-xl font-bold flex items-center">
63
+ <i data-feather="hexagon" class="mr-2"></i>
64
+ Čebelarstvo Cigoj
65
+ </h1>
66
+ <p class="text-xs text-amber-200 mt-1">Administracija</p>
67
+ </div>
68
+ <nav class="p-4">
69
+ <ul class="space-y-2">
70
+ <li>
71
+ <a href="#" class="sidebar-link flex items-center px-3 py-2 rounded-lg bg-amber-700 text-white">
72
+ <i data-feather="home" class="mr-3"></i>
73
+ Nadzorna plošča
74
+ </a>
75
+ </li>
76
+ <li>
77
+ <a href="products.php" class="sidebar-link flex items-center px-3 py-2 rounded-lg text-amber-200 hover:text-white">
78
+ <i data-feather="shopping-bag" class="mr-3"></i>
79
+ Izdelki
80
+ </a>
81
+ </li>
82
+ <li>
83
+ <a href="orders.php" class="sidebar-link flex items-center px-3 py-2 rounded-lg text-amber-200 hover:text-white">
84
+ <i data-feather="file-text" class="mr-3"></i>
85
+ Naročila
86
+ </a>
87
+ </li>
88
+ <li>
89
+ <a href="customers.php" class="sidebar-link flex items-center px-3 py-2 rounded-lg text-amber-200 hover:text-white">
90
+ <i data-feather="users" class="mr-3"></i>
91
+ Stranke
92
+ </a>
93
+ </li>
94
+ <li>
95
+ <a href="reports.php" class="sidebar-link flex items-center px-3 py-2 rounded-lg text-amber-200 hover:text-white">
96
+ <i data-feather="bar-chart-2" class="mr-3"></i>
97
+ Poročila
98
+ </a>
99
+ </li>
100
+ <li>
101
+ <a href="settings.php" class="sidebar-link flex items-center px-3 py-2 rounded-lg text-amber-200 hover:text-white">
102
+ <i data-feather="settings" class="mr-3"></i>
103
+ Nastavitve
104
+ </a>
105
+ </li>
106
+ </ul>
107
+ </nav>
108
+ </div>
109
+
110
+ <!-- Main Content -->
111
+ <div class="flex-1 overflow-auto">
112
+ <!-- Top Navigation -->
113
+ <header class="bg-white shadow-sm">
114
+ <div class="flex justify-between items-center p-4">
115
+ <div class="flex items-center">
116
+ <button class="p-2 rounded-full hover:bg-gray-100 mr-2">
117
+ <i data-feather="menu"></i>
118
+ </button>
119
+ <h2 class="text-lg font-semibold">Nadzorna plošča</h2>
120
+ </div>
121
+ <div class="flex items-center space-x-4">
122
+ <button class="p-2 rounded-full hover:bg-gray-100 relative">
123
+ <i data-feather="bell"></i>
124
+ <span class="absolute top-0 right-0 h-2 w-2 rounded-full bg-red-500"></span>
125
+ </button>
126
+ <div class="flex items-center">
127
+ <div class="w-8 h-8 rounded-full bg-amber-200 flex items-center justify-center mr-2">
128
+ <i data-feather="user"></i>
129
+ </div>
130
+ <span class="text-sm">Admin</span>
131
+ </div>
132
+ </div>
133
+ </div>
134
+ </header>
135
+
136
+ <!-- Dashboard Content -->
137
+ <main class="p-6">
138
+ <!-- Stats Cards -->
139
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
140
+ <div class="dashboard-card bg-white rounded-lg shadow p-6">
141
+ <div class="flex justify-between items-start">
142
+ <div>
143
+ <p class="text-gray-500 text-sm">Skupni prihodek</p>
144
+ <h3 class="text-2xl font-bold mt-1"><?php echo htmlspecialchars(number_format((float)$revenue, 2, ',', '.')) . '€'; ?></h3>
145
+ </div>
146
+ <div class="p-3 rounded-full bg-green-100 text-green-600">
147
+ <i data-feather="dollar-sign"></i>
148
+ </div>
149
+ </div>
150
+ <p class="text-green-600 text-sm mt-2 flex items-center">
151
+ <i data-feather="trending-up" class="mr-1"></i> V primerjavi s prejšnjim mesecem
152
+ </p>
153
+ </div>
154
+
155
+ <div class="dashboard-card bg-white rounded-lg shadow p-6">
156
+ <div class="flex justify-between items-start">
157
+ <div>
158
+ <p class="text-gray-500 text-sm">Naročila</p>
159
+ <h3 class="text-2xl font-bold mt-1"><?php echo htmlspecialchars((int)$orders_count); ?></h3>
160
+ </div>
161
+ <div class="p-3 rounded-full bg-blue-100 text-blue-600">
162
+ <i data-feather="shopping-cart"></i>
163
+ </div>
164
+ </div>
165
+ <p class="text-blue-600 text-sm mt-2 flex items-center">
166
+ <i data-feather="trending-up" class="mr-1"></i> Zadnjih <?php echo count($recentOrders); ?> naročil
167
+ </p>
168
+ </div>
169
+
170
+ <div class="dashboard-card bg-white rounded-lg shadow p-6">
171
+ <div class="flex justify-between items-start">
172
+ <div>
173
+ <p class="text-gray-500 text-sm">Izdelki</p>
174
+ <h3 class="text-2xl font-bold mt-1"><?php echo htmlspecialchars((int)$products_count); ?></h3>
175
+ </div>
176
+ <div class="p-3 rounded-full bg-purple-100 text-purple-600">
177
+ <i data-feather="package"></i>
178
+ </div>
179
+ </div>
180
+ <p class="text-purple-600 text-sm mt-2 flex items-center">
181
+ <i data-feather="alert-circle" class="mr-1"></i> Pregled zalog
182
+ </p>
183
+ </div>
184
+
185
+ <div class="dashboard-card bg-white rounded-lg shadow p-6">
186
+ <div class="flex justify-between items-start">
187
+ <div>
188
+ <p class="text-gray-500 text-sm">Stranke</p>
189
+ <h3 class="text-2xl font-bold mt-1"><?php echo htmlspecialchars((int)$customers_count); ?></h3>
190
+ </div>
191
+ <div class="p-3 rounded-full bg-amber-100 text-amber-600">
192
+ <i data-feather="users"></i>
193
+ </div>
194
+ </div>
195
+ <p class="text-amber-600 text-sm mt-2 flex items-center">
196
+ <i data-feather="trending-up" class="mr-1"></i> Novi uporabniki
197
+ </p>
198
+ </div>
199
+ </div>
200
+
201
+ <!-- Recent Orders -->
202
+ <div class="bg-white rounded-lg shadow overflow-hidden mb-8">
203
+ <div class="p-4 border-b flex justify-between items-center">
204
+ <h3 class="font-semibold">Zadnja naročila</h3>
205
+ <a href="#" class="text-sm text-amber-600 hover:text-amber-800">Prikaži vse</a>
206
+ </div>
207
+ <div class="overflow-x-auto">
208
+ <table class="min-w-full divide-y divide-gray-200">
209
+ <thead class="bg-gray-50">
210
+ <tr>
211
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Št. naročila</th>
212
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Kupec</th>
213
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
214
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Znesek</th>
215
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Datum</th>
216
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"></th>
217
+ </tr>
218
+ </thead>
219
+ <tbody class="bg-white divide-y divide-gray-200">
220
+ <?php if (empty($recentOrders)): ?>
221
+ <tr>
222
+ <td colspan="6" class="px-6 py-4 text-sm text-gray-500">Ni naročil za prikaz.</td>
223
+ </tr>
224
+ <?php else: ?>
225
+ <?php foreach ($recentOrders as $order): ?>
226
+ <tr>
227
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">#<?php echo htmlspecialchars($order['id']); ?></td>
228
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"><?php echo htmlspecialchars($order['customer_name']); ?></td>
229
+ <td class="px-6 py-4 whitespace-nowrap">
230
+ <?php
231
+ $status = $order['status'] ?? 'pending';
232
+ $badgeClass = 'bg-gray-100 text-gray-800';
233
+ if ($status === 'paid' || $status === 'completed') $badgeClass = 'bg-green-100 text-green-800';
234
+ if ($status === 'pending') $badgeClass = 'bg-yellow-100 text-yellow-800';
235
+ if ($status === 'shipped' || $status === 'sent') $badgeClass = 'bg-blue-100 text-blue-800';
236
+ ?>
237
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <?php echo $badgeClass; ?>"><?php echo htmlspecialchars(ucfirst($status)); ?></span>
238
+ </td>
239
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"><?php echo htmlspecialchars(number_format((float)$order['total_amount'], 2, ',', '.')) . '€'; ?></td>
240
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"><?php echo htmlspecialchars($order['created_at']); ?></td>
241
+ <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
242
+ <a href="orders.php?id=<?php echo urlencode($order['id']); ?>" class="text-amber-600 hover:text-amber-900">Ogled</a>
243
+ </td>
244
+ </tr>
245
+ <?php endforeach; ?>
246
+ <?php endif; ?>
247
+ </tbody>
248
+ </table>
249
+ </div>
250
+ </div>
251
+
252
+ <!-- Low Stock Products -->
253
+ <div class="bg-white rounded-lg shadow overflow-hidden">
254
+ <div class="p-4 border-b flex justify-between items-center">
255
+ <h3 class="font-semibold">Izdelki z nizko zalogo</h3>
256
+ <a href="#" class="text-sm text-amber-600 hover:text-amber-800">Prikaži vse</a>
257
+ </div>
258
+ <div class="overflow-x-auto">
259
+ <table class="min-w-full divide-y divide-gray-200">
260
+ <thead class="bg-gray-50">
261
+ <tr>
262
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Izdelek</th>
263
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
264
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Zaloga</th>
265
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
266
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"></th>
267
+ </tr>
268
+ </thead>
269
+ <tbody class="bg-white divide-y divide-gray-200">
270
+ <tr>
271
+ <td class="px-6 py-4 whitespace-nowrap">
272
+ <div class="flex items-center">
273
+ <div class="flex-shrink-0 h-10 w-10">
274
+ <img class="h-10 w-10 rounded" src="https://static.photos/nature/200x200/101" alt="">
275
+ </div>
276
+ <div class="ml-4">
277
+ <div class="text-sm font-medium text-gray-900">Cvetni prah 50g</div>
278
+ <div class="text-sm text-gray-500">4,50€</div>
279
+ </div>
280
+ </div>
281
+ </td>
282
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">PRD-001</td>
283
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">3</td>
284
+ <td class="px-6 py-4 whitespace-nowrap">
285
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">Nizka zaloga</span>
286
+ </td>
287
+ <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
288
+ <a href="#" class="text-amber-600 hover:text-amber-900">Uredi</a>
289
+ </td>
290
+ </tr>
291
+ <tr>
292
+ <td class="px-6 py-4 whitespace-nowrap">
293
+ <div class="flex items-center">
294
+ <div class="flex-shrink-0 h-10 w-10">
295
+ <img class="h-10 w-10 rounded" src="https://static.photos/nature/200x200/102" alt="">
296
+ </div>
297
+ <div class="ml-4">
298
+ <div class="text-sm font-medium text-gray-900">Balzam za ustnice</div>
299
+ <div class="text-sm text-gray-500">2,50€</div>
300
+ </div>
301
+ </div>
302
+ </td>
303
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">PRD-005</td>
304
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">5</td>
305
+ <td class="px-6 py-4 whitespace-nowrap">
306
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">Opozorilo</span>
307
+ </td>
308
+ <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
309
+ <a href="#" class="text-amber-600 hover:text-amber-900">Uredi</a>
310
+ </td>
311
+ </tr>
312
+ </tbody>
313
+ </table>
314
+ </div>
315
+ </div>
316
+ </main>
317
+ </div>
318
+ </div>
319
+
320
+ <script>
321
+ feather.replace();
322
+ </script>
323
+ </body>
324
+ </html>
cart.html ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="sl">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Košarica | Čebelarstvo Cigoj</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
9
+ </head>
10
+ <body class="font-sans bg-gray-50">
11
+ <!-- Header -->
12
+ <header class="bg-amber-800 text-white shadow-md">
13
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
14
+ <div class="flex justify-between items-center py-4">
15
+ <div class="flex items-center space-x-2">
16
+ <i data-feather="hexagon" class="text-amber-300"></i>
17
+ <h1 class="text-xl font-bold">Čebelarstvo Cigoj</h1>
18
+ </div>
19
+ <div class="flex items-center space-x-4">
20
+ <div class="relative">
21
+ <i data-feather="shopping-cart" class="text-amber-200 w-6 h-6"></i>
22
+ <span id="cart-count" class="absolute -top-2 -right-2 bg-amber-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center hidden">0</span>
23
+ </div>
24
+ <a href="index.html" class="text-amber-200 hover:text-white transition">
25
+ <i data-feather="arrow-left" class="mr-2"></i>Nazaj v trgovino
26
+ </a>
27
+ </div>
28
+ </div>
29
+ </div>
30
+ </header>
31
+
32
+ <!-- Cart Content -->
33
+ <main class="container mx-auto px-4 py-8">
34
+ <div class="max-w-4xl mx-auto">
35
+ <h2 class="text-2xl font-bold text-amber-900 mb-6">Vaša košarica</h2>
36
+
37
+ <div id="cart-content">
38
+ <!-- Cart items will be loaded here by JavaScript -->
39
+ </div>
40
+
41
+ <div id="cart-summary" class="bg-white rounded-lg shadow p-6 mt-6 hidden">
42
+ <div class="flex justify-between items-center text-lg font-bold mb-4">
43
+ <span>Skupaj:</span>
44
+ <span id="total-amount">0.00€</span>
45
+ </div>
46
+ <button onclick="proceedToCheckout()" class="w-full bg-amber-600 hover:bg-amber-700 text-white py-3 px-4 rounded-lg font-medium transition">
47
+ Nadaljuj s plačilom
48
+ </button>
49
+ </div>
50
+
51
+ <div id="empty-cart" class="bg-white rounded-lg shadow p-6 text-center hidden">
52
+ <p class="text-gray-600 mb-4">Vaša košarica je prazna</p>
53
+ <a href="index.html" class="text-amber-600 hover:text-amber-800">Nazaj v trgovino</a>
54
+ </div>
55
+ </div>
56
+ </main>
57
+
58
+ <script>
59
+ function loadCart() {
60
+ const cart = JSON.parse(localStorage.getItem('cart')) || [];
61
+ const cartContent = document.getElementById('cart-content');
62
+ const cartSummary = document.getElementById('cart-summary');
63
+ const emptyCart = document.getElementById('empty-cart');
64
+
65
+ if (cart.length === 0) {
66
+ emptyCart.classList.remove('hidden');
67
+ cartSummary.classList.add('hidden');
68
+ return;
69
+ }
70
+
71
+ let total = 0;
72
+ let html = '<div class="bg-white rounded-lg shadow overflow-hidden">';
73
+ html += '<div class="grid grid-cols-4 gap-4 p-4 bg-gray-50 font-medium">';
74
+ html += '<div>Izdelek</div><div>Cena</div><div>Količina</div><div>Skupaj</div>';
75
+ html += '</div>';
76
+
77
+ cart.forEach((item, index) => {
78
+ const itemTotal = item.price * item.quantity;
79
+ total += itemTotal;
80
+
81
+ html += `<div class="grid grid-cols-4 gap-4 p-4 border-t border-gray-200 items-center">`;
82
+ html += `<div class="font-medium">${item.name}</div>`;
83
+ html += `<div>${item.price.toFixed(2)}€</div>`;
84
+ html += `<div class="flex items-center space-x-2">`;
85
+ html += `<button onclick="updateQuantity(${index}, -1)" class="bg-gray-200 hover:bg-gray-300 w-8 h-8 rounded flex items-center justify-center">-</button>`;
86
+ html += `<span class="mx-2">${item.quantity}</span>`;
87
+ html += `<button onclick="updateQuantity(${index}, 1)" class="bg-gray-200 hover:bg-gray-300 w-8 h-8 rounded flex items-center justify-center">+</button>`;
88
+ html += `</div>`;
89
+ html += `<div class="flex justify-between items-center">`;
90
+ html += `<span>${itemTotal.toFixed(2)}€</span>`;
91
+ html += `<button onclick="removeItem(${index})" class="text-red-600 hover:text-red-800 ml-4">`;
92
+ html += `<i data-feather="trash-2"></i></button>`;
93
+ html += `</div></div>`;
94
+ });
95
+
96
+ html += '</div>';
97
+ cartContent.innerHTML = html;
98
+ document.getElementById('total-amount').textContent = total.toFixed(2) + '€';
99
+ cartSummary.classList.remove('hidden');
100
+
101
+ feather.replace();
102
+ }
103
+
104
+ function updateQuantity(index, change) {
105
+ let cart = JSON.parse(localStorage.getItem('cart')) || [];
106
+ cart[index].quantity += change;
107
+
108
+ if (cart[index].quantity <= 0) {
109
+ cart.splice(index, 1);
110
+ }
111
+
112
+ localStorage.setItem('cart', JSON.stringify(cart));
113
+ loadCart();
114
+ updateCartCount();
115
+ }
116
+
117
+ function removeItem(index) {
118
+ let cart = JSON.parse(localStorage.getItem('cart')) || [];
119
+ cart.splice(index, 1);
120
+ localStorage.setItem('cart', JSON.stringify(cart));
121
+ loadCart();
122
+ updateCartCount();
123
+ }
124
+
125
+ function updateCartCount() {
126
+ const cart = JSON.parse(localStorage.getItem('cart')) || [];
127
+ const count = cart.reduce((total, item) => total + item.quantity, 0);
128
+ const cartCount = document.getElementById('cart-count');
129
+
130
+ if (cartCount) {
131
+ cartCount.textContent = count;
132
+ if(count > 0) {
133
+ cartCount.classList.remove('hidden');
134
+ } else {
135
+ cartCount.classList.add('hidden');
136
+ }
137
+ }
138
+ }
139
+
140
+ function proceedToCheckout() {
141
+ const cart = JSON.parse(localStorage.getItem('cart')) || [];
142
+ if (cart.length === 0) {
143
+ alert('Vaša košarica je prazna');
144
+ return;
145
+ }
146
+ // Redirect to checkout page
147
+ window.location.href = 'checkout.php';
148
+ }
149
+
150
+ // Load cart on page load
151
+ loadCart();
152
+ feather.replace();
153
+ </script>
154
+ </body>
155
+ </html>
cebelarstvo_cigoj (1).db ADDED
File without changes
cebelarstvo_cigoj.db ADDED
Binary file (45.1 kB). View file
 
checkout.php ADDED
@@ -0,0 +1,337 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once 'config.php';
3
+
4
+ // Handle checkout form submission
5
+ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
6
+ header('Content-Type: application/json');
7
+
8
+ $input = json_decode(file_get_contents('php://input'), true);
9
+
10
+ // Validate required fields
11
+ $required_fields = ['customer_name', 'customer_email', 'customer_address', 'customer_phone', 'cart_items'];
12
+ foreach ($required_fields as $field) {
13
+ if (empty($input[$field])) {
14
+ http_response_code(400);
15
+ echo json_encode(['error' => "Field $field is required"]);
16
+ exit;
17
+ }
18
+ }
19
+
20
+ $customer_name = trim($input['customer_name']);
21
+ $customer_email = filter_var($input['customer_email'], FILTER_VALIDATE_EMAIL);
22
+ $customer_address = trim($input['customer_address']);
23
+ $customer_phone = trim($input['customer_phone']);
24
+ $cart_items = $input['cart_items'];
25
+ $shipping_method = $input['shipping_method'] ?? 'posta_slovenije';
26
+
27
+ if (!$customer_email) {
28
+ http_response_code(400);
29
+ echo json_encode(['error' => 'Invalid email address']);
30
+ exit;
31
+ }
32
+
33
+ // Calculate totals
34
+ $subtotal = 0;
35
+ foreach ($cart_items as $item) {
36
+ $subtotal += $item['price'] * $item['quantity'];
37
+ }
38
+
39
+ // Add shipping cost (Pošta Slovenije - 4.90 euro)
40
+ $shipping_cost = 0;
41
+ if ($shipping_method === 'posta_slovenije') {
42
+ $shipping_cost = 4.90;
43
+ }
44
+
45
+ $total = $subtotal + $shipping_cost;
46
+
47
+ try {
48
+ $pdo->beginTransaction();
49
+
50
+ // Create order
51
+ $stmt = $pdo->prepare("INSERT INTO orders (customer_name, customer_email, customer_address, customer_phone, subtotal, shipping_cost, total_amount, shipping_method, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pending', CURRENT_TIMESTAMP)");
52
+ $stmt->execute([
53
+ $customer_name,
54
+ $customer_email,
55
+ $customer_address,
56
+ $customer_phone,
57
+ $subtotal,
58
+ $shipping_cost,
59
+ $total,
60
+ $shipping_method
61
+ ]);
62
+
63
+ $order_id = $pdo->lastInsertId();
64
+
65
+ // Add order items
66
+ foreach ($cart_items as $item) {
67
+ $stmt = $pdo->prepare("INSERT INTO order_items (order_id, product_id, product_name, quantity, unit_price, total_price) VALUES (?, ?, ?, ?, ?, ?)");
68
+ $stmt->execute([
69
+ $order_id,
70
+ $item['id'],
71
+ $item['name'],
72
+ $item['quantity'],
73
+ $item['price'],
74
+ $item['price'] * $item['quantity']
75
+ ]);
76
+ }
77
+
78
+ $pdo->commit();
79
+
80
+ echo json_encode([
81
+ 'success' => true,
82
+ 'order_id' => $order_id,
83
+ 'subtotal' => $subtotal,
84
+ 'shipping_cost' => $shipping_cost,
85
+ 'total' => $total,
86
+ 'message' => 'Naročilo je bilo uspešno oddano!'
87
+ ]);
88
+
89
+ } catch (Exception $e) {
90
+ $pdo->rollBack();
91
+ http_response_code(500);
92
+ echo json_encode(['error' => 'Napaka pri oddaji naročila: ' . $e->getMessage()]);
93
+ }
94
+
95
+ exit;
96
+ }
97
+
98
+ // Show checkout form
99
+ ?>
100
+ <!DOCTYPE html>
101
+ <html lang="sl">
102
+ <head>
103
+ <meta charset="UTF-8">
104
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
105
+ <title>Blagajna | Čebelarstvo Cigoj</title>
106
+ <script src="https://cdn.tailwindcss.com"></script>
107
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
108
+ <style>
109
+ .form-input:focus {
110
+ outline: none;
111
+ border-color: #d97706;
112
+ box-shadow: 0 0 0 3px rgba(217, 119, 6, 0.1);
113
+ }
114
+ </style>
115
+ </head>
116
+ <body class="bg-gray-50 font-sans">
117
+ <!-- Navigation -->
118
+ <nav class="bg-amber-800 text-white p-4">
119
+ <div class="max-w-7xl mx-auto flex justify-between items-center">
120
+ <div class="flex items-center">
121
+ <i data-feather="hexagon" class="mr-2"></i>
122
+ <h1 class="text-xl font-bold">Čebelarstvo Cigoj</h1>
123
+ </div>
124
+ <a href="index.html" class="flex items-center text-amber-200 hover:text-white">
125
+ <i data-feather="arrow-left" class="mr-1"></i>
126
+ Nazaj v trgovino
127
+ </a>
128
+ </div>
129
+ </nav>
130
+
131
+ <!-- Checkout Form -->
132
+ <div class="max-w-4xl mx-auto p-6">
133
+ <div class="bg-white rounded-lg shadow-lg overflow-hidden">
134
+ <div class="bg-amber-50 px-6 py-4 border-b">
135
+ <h2 class="text-2xl font-bold text-amber-900">Blagajna</h2>
136
+ <p class="text-amber-700 mt-1">Vnesite podatke za oddajo naročila</p>
137
+ </div>
138
+
139
+ <div class="grid md:grid-cols-2 gap-8 p-6">
140
+ <!-- Customer Information -->
141
+ <div>
142
+ <h3 class="text-lg font-semibold text-gray-900 mb-4">Podatki za dostavo</h3>
143
+ <form id="checkout-form" class="space-y-4">
144
+ <div>
145
+ <label for="customer_name" class="block text-sm font-medium text-gray-700 mb-1">Ime in priimek *</label>
146
+ <input type="text" id="customer_name" name="customer_name" required
147
+ class="form-input w-full px-3 py-2 border border-gray-300 rounded-md">
148
+ </div>
149
+
150
+ <div>
151
+ <label for="customer_email" class="block text-sm font-medium text-gray-700 mb-1">E-mail naslov *</label>
152
+ <input type="email" id="customer_email" name="customer_email" required
153
+ class="form-input w-full px-3 py-2 border border-gray-300 rounded-md">
154
+ </div>
155
+
156
+ <div>
157
+ <label for="customer_phone" class="block text-sm font-medium text-gray-700 mb-1">Telefon *</label>
158
+ <input type="tel" id="customer_phone" name="customer_phone" required
159
+ class="form-input w-full px-3 py-2 border border-gray-300 rounded-md">
160
+ </div>
161
+
162
+ <div>
163
+ <label for="customer_address" class="block text-sm font-medium text-gray-700 mb-1">Naslov za dostavo *</label>
164
+ <textarea id="customer_address" name="customer_address" rows="3" required
165
+ class="form-input w-full px-3 py-2 border border-gray-300 rounded-md"></textarea>
166
+ </div>
167
+
168
+ <!-- Shipping Method -->
169
+ <div>
170
+ <label class="block text-sm font-medium text-gray-700 mb-3">Način dostave</label>
171
+ <div class="space-y-2">
172
+ <label class="flex items-center p-3 border border-amber-200 rounded-lg bg-amber-50">
173
+ <input type="radio" name="shipping_method" value="posta_slovenije" checked
174
+ class="text-amber-600 focus:ring-amber-500">
175
+ <div class="ml-3 flex-1">
176
+ <div class="flex justify-between items-center">
177
+ <span class="font-medium text-gray-900">Pošta Slovenije</span>
178
+ <span class="font-bold text-amber-700">4,90 €</span>
179
+ </div>
180
+ <p class="text-sm text-gray-600">Standardna dostava v 2-3 delovnih dneh</p>
181
+ </div>
182
+ </label>
183
+ </div>
184
+ </div>
185
+ </form>
186
+ </div>
187
+
188
+ <!-- Order Summary -->
189
+ <div>
190
+ <h3 class="text-lg font-semibold text-gray-900 mb-4">Povzetek naročila</h3>
191
+ <div class="bg-gray-50 rounded-lg p-4">
192
+ <div id="order-items" class="space-y-3 mb-4">
193
+ <!-- Items will be populated by JavaScript -->
194
+ </div>
195
+
196
+ <div class="border-t pt-4 space-y-2">
197
+ <div class="flex justify-between text-sm">
198
+ <span>Izdelki:</span>
199
+ <span id="subtotal">0,00 €</span>
200
+ </div>
201
+ <div class="flex justify-between text-sm">
202
+ <span>Dostava (Pošta Slovenije):</span>
203
+ <span>4,90 €</span>
204
+ </div>
205
+ <div class="flex justify-between font-bold text-lg border-t pt-2">
206
+ <span>Skupaj:</span>
207
+ <span id="total">4,90 €</span>
208
+ </div>
209
+ </div>
210
+
211
+ <button type="submit" form="checkout-form"
212
+ class="w-full mt-6 bg-amber-600 hover:bg-amber-700 text-white font-medium py-3 px-4 rounded-lg transition duration-200">
213
+ <i data-feather="credit-card" class="inline mr-2"></i>
214
+ Oddaj naročilo
215
+ </button>
216
+
217
+ <p class="text-xs text-gray-500 mt-3 text-center">
218
+ * Plačilo ob prevzemu (gotovina ali kartica)
219
+ </p>
220
+ </div>
221
+ </div>
222
+ </div>
223
+ </div>
224
+ </div>
225
+
226
+ <!-- Success Modal -->
227
+ <div id="success-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
228
+ <div class="bg-white rounded-lg p-8 max-w-md mx-4">
229
+ <div class="text-center">
230
+ <div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100 mb-4">
231
+ <i data-feather="check" class="text-green-600"></i>
232
+ </div>
233
+ <h3 class="text-lg font-medium text-gray-900 mb-2">Naročilo uspešno oddano!</h3>
234
+ <p class="text-gray-500 mb-6">Vaše naročilo št. <span id="order-number"></span> je bilo prejeto. Kontaktirali vas bomo v kratkem.</p>
235
+ <button onclick="window.location.href='index.html'"
236
+ class="bg-amber-600 hover:bg-amber-700 text-white px-6 py-2 rounded-lg">
237
+ Nazaj v trgovino
238
+ </button>
239
+ </div>
240
+ </div>
241
+ </div>
242
+
243
+ <script>
244
+ feather.replace();
245
+
246
+ // Load cart items from localStorage
247
+ function loadCartItems() {
248
+ const cart = JSON.parse(localStorage.getItem('cart') || '[]');
249
+ const orderItemsDiv = document.getElementById('order-items');
250
+ const subtotalSpan = document.getElementById('subtotal');
251
+ const totalSpan = document.getElementById('total');
252
+
253
+ if (cart.length === 0) {
254
+ orderItemsDiv.innerHTML = '<p class="text-gray-500 text-center">Košarica je prazna</p>';
255
+ return;
256
+ }
257
+
258
+ let subtotal = 0;
259
+ orderItemsDiv.innerHTML = '';
260
+
261
+ cart.forEach(item => {
262
+ const itemTotal = item.price * item.quantity;
263
+ subtotal += itemTotal;
264
+
265
+ const itemDiv = document.createElement('div');
266
+ itemDiv.className = 'flex justify-between items-center';
267
+ itemDiv.innerHTML = `
268
+ <div>
269
+ <span class="font-medium">${item.name}</span>
270
+ <span class="text-gray-500 text-sm">× ${item.quantity}</span>
271
+ </div>
272
+ <span>${itemTotal.toFixed(2)} €</span>
273
+ `;
274
+ orderItemsDiv.appendChild(itemDiv);
275
+ });
276
+
277
+ const shipping = 4.90;
278
+ const total = subtotal + shipping;
279
+
280
+ subtotalSpan.textContent = subtotal.toFixed(2) + ' €';
281
+ totalSpan.textContent = total.toFixed(2) + ' €';
282
+ }
283
+
284
+ // Handle form submission
285
+ document.getElementById('checkout-form').addEventListener('submit', async function(e) {
286
+ e.preventDefault();
287
+
288
+ const formData = new FormData(this);
289
+ const cart = JSON.parse(localStorage.getItem('cart') || '[]');
290
+
291
+ if (cart.length === 0) {
292
+ alert('Košarica je prazna!');
293
+ return;
294
+ }
295
+
296
+ const data = {
297
+ customer_name: formData.get('customer_name'),
298
+ customer_email: formData.get('customer_email'),
299
+ customer_phone: formData.get('customer_phone'),
300
+ customer_address: formData.get('customer_address'),
301
+ shipping_method: formData.get('shipping_method'),
302
+ cart_items: cart
303
+ };
304
+
305
+ try {
306
+ const response = await fetch('checkout.php', {
307
+ method: 'POST',
308
+ headers: {
309
+ 'Content-Type': 'application/json'
310
+ },
311
+ body: JSON.stringify(data)
312
+ });
313
+
314
+ const result = await response.json();
315
+
316
+ if (response.ok && result.success) {
317
+ // Clear cart
318
+ localStorage.removeItem('cart');
319
+
320
+ // Show success modal
321
+ document.getElementById('order-number').textContent = result.order_id;
322
+ document.getElementById('success-modal').classList.remove('hidden');
323
+ document.getElementById('success-modal').classList.add('flex');
324
+ } else {
325
+ alert(result.error || 'Napaka pri oddaji naročila');
326
+ }
327
+ } catch (error) {
328
+ alert('Napaka pri povezavi s strežnikom');
329
+ console.error('Error:', error);
330
+ }
331
+ });
332
+
333
+ // Load cart items on page load
334
+ loadCartItems();
335
+ </script>
336
+ </body>
337
+ </html>
composer.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "require": {
3
+ "firebase/php-jwt": "^6.11"
4
+ }
5
+ }
composer.lock ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_readme": [
3
+ "This file locks the dependencies of your project to a known state",
4
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5
+ "This file is @generated automatically"
6
+ ],
7
+ "content-hash": "726b0a650f3a35c706f5a399d9e99662",
8
+ "packages": [
9
+ {
10
+ "name": "firebase/php-jwt",
11
+ "version": "v6.11.1",
12
+ "source": {
13
+ "type": "git",
14
+ "url": "https://github.com/firebase/php-jwt.git",
15
+ "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66"
16
+ },
17
+ "dist": {
18
+ "type": "zip",
19
+ "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
20
+ "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
21
+ "shasum": ""
22
+ },
23
+ "require": {
24
+ "php": "^8.0"
25
+ },
26
+ "require-dev": {
27
+ "guzzlehttp/guzzle": "^7.4",
28
+ "phpspec/prophecy-phpunit": "^2.0",
29
+ "phpunit/phpunit": "^9.5",
30
+ "psr/cache": "^2.0||^3.0",
31
+ "psr/http-client": "^1.0",
32
+ "psr/http-factory": "^1.0"
33
+ },
34
+ "suggest": {
35
+ "ext-sodium": "Support EdDSA (Ed25519) signatures",
36
+ "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
37
+ },
38
+ "type": "library",
39
+ "autoload": {
40
+ "psr-4": {
41
+ "Firebase\\JWT\\": "src"
42
+ }
43
+ },
44
+ "notification-url": "https://packagist.org/downloads/",
45
+ "license": [
46
+ "BSD-3-Clause"
47
+ ],
48
+ "authors": [
49
+ {
50
+ "name": "Neuman Vong",
51
+ "email": "neuman+pear@twilio.com",
52
+ "role": "Developer"
53
+ },
54
+ {
55
+ "name": "Anant Narayanan",
56
+ "email": "anant@php.net",
57
+ "role": "Developer"
58
+ }
59
+ ],
60
+ "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
61
+ "homepage": "https://github.com/firebase/php-jwt",
62
+ "keywords": [
63
+ "jwt",
64
+ "php"
65
+ ],
66
+ "support": {
67
+ "issues": "https://github.com/firebase/php-jwt/issues",
68
+ "source": "https://github.com/firebase/php-jwt/tree/v6.11.1"
69
+ },
70
+ "time": "2025-04-09T20:32:01+00:00"
71
+ }
72
+ ],
73
+ "packages-dev": [],
74
+ "aliases": [],
75
+ "minimum-stability": "stable",
76
+ "stability-flags": [],
77
+ "prefer-stable": false,
78
+ "prefer-lowest": false,
79
+ "platform": [],
80
+ "platform-dev": [],
81
+ "plugin-api-version": "2.6.0"
82
+ }
config.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Include Composer autoloader
3
+ require_once __DIR__ . '/vendor/autoload.php';
4
+
5
+ // Use Firebase JWT
6
+ use Firebase\JWT\JWT;
7
+ use Firebase\JWT\Key;
8
+
9
+ // Show errors for development (disable in production)
10
+ ini_set('display_errors', 1);
11
+ ini_set('display_startup_errors', 1);
12
+ error_reporting(E_ALL);
13
+
14
+ // Database configuration - using SQLite for development in Replit
15
+ define('DB_HOST', '');
16
+ define('DB_USER', '');
17
+ define('DB_PASS', '');
18
+ define('DB_NAME', 'cebelarstvo_cigoj.db');
19
+
20
+ // Establish database connection (SQLite for now)
21
+ try {
22
+ $pdo = new PDO("sqlite:" . __DIR__ . "/" . DB_NAME);
23
+ $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
24
+ // Enable foreign key support for SQLite
25
+ $pdo->exec('PRAGMA foreign_keys = ON;');
26
+ } catch(PDOException $e) {
27
+ die("ERROR: Could not connect to database. " . $e->getMessage());
28
+ }
29
+
30
+ // Set headers for API (only set when needed to avoid conflicts with HTML pages)
31
+ if (isset($_SERVER['REQUEST_URI']) &&
32
+ strpos($_SERVER['REQUEST_URI'], '.php') !== false &&
33
+ strpos($_SERVER['REQUEST_URI'], '.html') === false &&
34
+ !strpos($_SERVER['REQUEST_URI'], 'backend.html') &&
35
+ !strpos($_SERVER['REQUEST_URI'], 'backend.php') &&
36
+ !strpos($_SERVER['REQUEST_URI'], 'admin_login.php') &&
37
+ !strpos($_SERVER['REQUEST_URI'], 'checkout.php') &&
38
+ !strpos($_SERVER['REQUEST_URI'], 'thank_you.php')) {
39
+ header("Content-Type: application/json; charset=UTF-8");
40
+ // More secure CORS - restrict to localhost for development
41
+ $allowed_origins = ['http://localhost:5000', 'https://localhost:5000'];
42
+ $origin = $_SERVER['HTTP_ORIGIN'] ?? '';
43
+ if (in_array($origin, $allowed_origins)) {
44
+ header("Access-Control-Allow-Origin: " . $origin);
45
+ }
46
+ header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE");
47
+ header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
48
+ }
49
+
50
+ // JWT Secret key - use environment variable for security
51
+ $jwt_secret = 'your_hardcoded_secret_here';
52
+ define('JWT_SECRET', $jwt_secret);
53
+
54
+ // JWT verification middleware function - fixed for PHP built-in server
55
+ function verifyToken() {
56
+ // Use getallheaders() with fallback for PHP built-in server
57
+ $headers = function_exists('getallheaders') ? getallheaders() : [];
58
+
59
+ // Fallback for PHP built-in server
60
+ if (empty($headers) && isset($_SERVER['HTTP_AUTHORIZATION'])) {
61
+ $headers['Authorization'] = $_SERVER['HTTP_AUTHORIZATION'];
62
+ }
63
+
64
+ if(!isset($headers['Authorization'])) {
65
+ http_response_code(401);
66
+ echo json_encode(array("message" => "Access Denied. No token provided."));
67
+ exit;
68
+ }
69
+
70
+ try {
71
+ $token = str_replace('Bearer ', '', $headers['Authorization']);
72
+ $decoded = JWT::decode($token, new Key(JWT_SECRET, 'HS256'));
73
+ return $decoded;
74
+ } catch(Exception $e) {
75
+ http_response_code(401);
76
+ echo json_encode(array("message" => "Invalid token", "error" => $e->getMessage()));
77
+ exit;
78
+ }
79
+ }
80
+ ?>
customers.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <?php
3
+ require_once 'config.php';
4
+
5
+ // JWT verification middleware
6
+ require_once 'require_auth.php';
7
+
8
+ // GET all customers
9
+ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
10
+ $stmt = $pdo->query("SELECT * FROM customers");
11
+ $customers = $stmt->fetchAll(PDO::FETCH_ASSOC);
12
+ echo json_encode($customers);
13
+ }
14
+
15
+ // GET customer orders
16
+ if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['customer_id'])) {
17
+ $customer_id = $_GET['customer_id'];
18
+
19
+ $stmt = $pdo->prepare("SELECT * FROM orders WHERE customer_email = (SELECT email FROM customers WHERE id = ?)");
20
+ $stmt->execute([$customer_id]);
21
+ $orders = $stmt->fetchAll(PDO::FETCH_ASSOC);
22
+
23
+ echo json_encode($orders);
24
+ }
25
+ ?>
index.html CHANGED
@@ -1,19 +1,390 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="sl">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Premium Naravni Med in Čebelarski Izdelki | Čebelarstvo Cigoj</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
9
+ <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
11
+ <script src="https://unpkg.com/feather-icons"></script>
12
+ <style>
13
+ .aspect-w-1 {
14
+ position: relative;
15
+ padding-bottom: 100%;
16
+ }
17
+ .aspect-h-1 {
18
+ position: relative;
19
+ padding-bottom: 100%;
20
+ }
21
+ .aspect-w-1 > * {
22
+ position: absolute;
23
+ top: 0;
24
+ left: 0;
25
+ width: 100%;
26
+ height: 100%;
27
+ }
28
+ .group:hover .group-hover\:opacity-75 {
29
+ opacity: 0.75;
30
+ }
31
+ </style>
32
+ </head>
33
+ <body class="font-sans bg-gray-50">
34
+ <!-- Header -->
35
+ <header class="bg-amber-800 text-white shadow-md sticky top-0 z-50">
36
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
37
+ <div class="flex justify-between items-center py-4 md:justify-start md:space-x-10">
38
+ <div class="flex justify-start lg:w-0 lg:flex-1">
39
+ <div class="flex items-center space-x-2">
40
+ <i data-feather="hexagon" class="text-amber-300"></i>
41
+ <h1 class="text-xl font-bold">Čebelarstvo Cigoj</h1>
42
+ </div>
43
+ </div>
44
+ <nav class="hidden md:flex space-x-10">
45
+ <a href="#" class="text-base font-medium text-white hover:text-amber-200">Domov</a>
46
+ <a href="#" class="text-base font-medium text-white hover:text-amber-200">Izdelki</a>
47
+ <a href="#" class="text-base font-medium text-white hover:text-amber-200">O nas</a>
48
+ <a href="#" class="text-base font-medium text-white hover:text-amber-200">Kontakt</a>
49
+ </nav>
50
+ <div class="flex items-center justify-end md:flex-1 lg:w-0 space-x-6">
51
+ <a href="cart.html" class="relative p-2 rounded-full hover:bg-amber-700 transition" id="cart-button">
52
+ <i data-feather="shopping-cart"></i>
53
+ <span id="cart-count" class="hidden absolute -top-1 -right-1 bg-red-500 text-white text-xs font-bold rounded-full h-5 w-5 flex items-center justify-center">0</span>
54
+ </a>
55
+ <a href="admin_login.php" class="text-white hover:text-amber-200 px-3 py-2 rounded">Admin</a>
56
+ <button class="md:hidden p-2 rounded-full hover:bg-amber-700 transition">
57
+ <i data-feather="menu"></i>
58
+ </button>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ </header>
63
+
64
+ <!-- Hero Banner -->
65
+ <section class="bg-gradient-to-br from-amber-50 to-amber-100 py-16">
66
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
67
+ <div class="lg:grid lg:grid-cols-12 lg:gap-8 items-center">
68
+ <div class="sm:text-center md:max-w-2xl md:mx-auto lg:col-span-6 lg:text-left">
69
+ <h2 class="text-4xl tracking-tight font-extrabold text-amber-900 sm:text-5xl md:text-6xl">
70
+ <span class="block">Naravni cvetni prahovi</span>
71
+ </h2>
72
+ <p class="mt-3 text-lg text-amber-800 sm:mt-5 sm:text-xl sm:max-w-xl sm:mx-auto md:mt-5 md:text-xl lg:mx-0">
73
+ Izjemna superhrana direktno iz narave, bogata z vitamini in minerali.
74
+ </p>
75
+ <p class="mt-3 text-2xl font-bold text-amber-700">Že od 5€</p>
76
+ <div class="mt-8 sm:max-w-lg sm:mx-auto sm:text-center lg:text-left lg:mx-0">
77
+ <a href="#" class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-lg shadow-sm text-white bg-amber-600 hover:bg-amber-700 transition">
78
+ Oglejte si ponudbo
79
+ <i data-feather="arrow-right" class="ml-2"></i>
80
+ </a>
81
+ </div>
82
+ </div>
83
+ <div class="mt-12 relative sm:max-w-lg sm:mx-auto lg:mt-0 lg:max-w-none lg:mx-0 lg:col-span-6 lg:flex lg:items-center">
84
+ <div class="relative mx-auto w-full rounded-lg shadow-lg lg:max-w-md">
85
+ <img src="https://static.photos/nature/640x360/42" alt="Cvetni prah" class="object-cover rounded-lg w-full">
86
+ </div>
87
+ </div>
88
+ </div>
89
+ </div>
90
+ </section>
91
+
92
+ <!-- Featured Products -->
93
+ <section class="py-16 bg-white">
94
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
95
+ <h2 class="text-3xl font-bold text-center text-amber-900 mb-12" data-aos="fade-up">Priljubljeni izdelki</h2>
96
+
97
+ <div class="grid grid-cols-1 gap-y-10 gap-x-6 sm:grid-cols-2 lg:grid-cols-4 xl:gap-x-8">
98
+ <!-- Product 1 -->
99
+ <div class="product-card bg-white rounded-lg overflow-hidden shadow-md relative transition duration-300" data-aos="fade-up" data-aos-delay="100">
100
+ <div class="product-card bg-white rounded-lg overflow-hidden shadow-md relative transition duration-300" data-aos="fade-up" data-aos-delay="100" data-product-id="1">
101
+ <div class="w-full min-h-80 bg-gray-200 aspect-w-1 aspect-h-1 rounded-md overflow-hidden group-hover:opacity-75 lg:h-80 lg:aspect-none">
102
+ <img src="https://static.photos/nature/320x240/101" alt="Cvetni prah 50g" class="w-full h-full object-center object-cover lg:w-full lg:h-full">
103
+ </div>
104
+ <div class="mt-4 flex justify-between">
105
+ <div>
106
+ <h3 class="text-lg text-amber-900 font-bold">
107
+ <a href="#">
108
+ <span aria-hidden="true" class="absolute inset-0"></span>
109
+ Cvetni prah 50g
110
+ </a>
111
+ </h3>
112
+ <p class="mt-1 text-sm text-gray-500">100g</p>
113
+ </div>
114
+ <p class="text-lg font-bold text-amber-700">4,50€</p>
115
+ </div>
116
+ <div class="mt-4 grid grid-cols-2 gap-2">
117
+ <button class="w-full bg-amber-100 hover:bg-amber-200 text-amber-800 py-2 px-2 rounded text-sm flex items-center justify-center transition">
118
+ <i data-feather="eye" class="mr-1 w-4 h-4"></i> <span>Ogled</span>
119
+ </button>
120
+ <button class="w-full bg-amber-600 hover:bg-amber-700 text-white py-2 px-2 rounded text-sm flex items-center justify-center transition">
121
+ <i data-feather="shopping-cart" class="mr-1 w-4 h-4"></i> <span>V košarico</span>
122
+ </button>
123
+ </div>
124
+ <div class="absolute top-2 right-2 bg-red-500 text-white text-xs font-bold px-2 py-1 rounded-full">
125
+ RAZPRODAJA!
126
+ </div>
127
+ </div>
128
+
129
+ <!-- Product 2 -->
130
+ <div class="product-card bg-white rounded-lg overflow-hidden shadow-md relative transition duration-300" data-aos="fade-up" data-aos-delay="200">
131
+ <img src="https://static.photos/nature/320x240/102" alt="Balzam za ustnice" class="w-full h-48 object-cover">
132
+ <div class="p-4">
133
+ <h3 class="font-bold text-lg mb-2 text-amber-900">Balzam za ustnice iz čebeljega voska</h3>
134
+ <p class="text-amber-700 font-bold text-xl mb-3">2,50€</p>
135
+ <div class="flex justify-between">
136
+ <button class="bg-amber-100 hover:bg-amber-200 text-amber-800 px-3 py-1 rounded text-sm flex items-center transition">
137
+ <i data-feather="eye" class="mr-1 w-4 h-4"></i> Hitri ogled
138
+ </button>
139
+ <button class="bg-amber-600 hover:bg-amber-700 text-white px-3 py-1 rounded text-sm flex items-center transition">
140
+ <i data-feather="shopping-cart" class="mr-1 w-4 h-4"></i> V košarico
141
+ </button>
142
+ </div>
143
+ </div>
144
+ </div>
145
+ </div>
146
+
147
+ <!-- Product 3 -->
148
+ <div class="product-card bg-white rounded-lg overflow-hidden shadow-md relative transition duration-300" data-aos="fade-up" data-aos-delay="300">
149
+ <div class="sale-badge bg-red-500 text-white text-xs font-bold px-2 py-1 rounded-full">
150
+ RAZPRODAJA!
151
+ </div>
152
+ <img src="https://static.photos/nature/320x240/103" alt="Cvetni prah 100g" class="w-full h-48 object-cover">
153
+ <div class="p-4">
154
+ <h3 class="font-bold text-lg mb-2 text-amber-900">Cvetni prah 100g</h3>
155
+ <p class="text-amber-700 font-bold text-xl mb-3">7,00€</p>
156
+ <div class="flex justify-between">
157
+ <button class="bg-amber-100 hover:bg-amber-200 text-amber-800 px-3 py-1 rounded text-sm flex items-center transition">
158
+ <i data-feather="eye" class="mr-1 w-4 h-4"></i> Hitri ogled
159
+ </button>
160
+ <button class="bg-amber-600 hover:bg-amber-700 text-white px-3 py-1 rounded text-sm flex items-center transition">
161
+ <i data-feather="shopping-cart" class="mr-1 w-4 h-4"></i> V košarico
162
+ </button>
163
+ </div>
164
+ </div>
165
+ </div>
166
+ </div>
167
+
168
+ <!-- Product 4 -->
169
+ <div class="product-card bg-white rounded-lg overflow-hidden shadow-md relative transition duration-300" data-aos="fade-up" data-aos-delay="400">
170
+ <div class="sale-badge bg-red-500 text-white text-xs font-bold px-2 py-1 rounded-full">
171
+ RAZPRODAJA!
172
+ </div>
173
+ <img src="https://static.photos/nature/320x240/104" alt="Balzam za ustnice" class="w-full h-48 object-cover">
174
+ <div class="p-4">
175
+ <h3 class="font-bold text-lg mb-2 text-amber-900">Balzam za ustnice iz čebeljega voska</h3>
176
+ <p class="text-amber-700 font-bold text-xl mb-3">2,50€</p>
177
+ <div class="flex justify-between">
178
+ <button class="bg-amber-100 hover:bg-amber-200 text-amber-800 px-3 py-1 rounded text-sm flex items-center transition">
179
+ <i data-feather="eye" class="mr-1 w-4 h-4"></i> Hitri ogled
180
+ </button>
181
+ <button class="bg-amber-600 hover:bg-amber-700 text-white px-3 py-1 rounded text-sm flex items-center transition">
182
+ <i data-feather="shopping-cart" class="mr-1 w-4 h-4"></i> V košarico
183
+ </button>
184
+ </div>
185
+ </div>
186
+ </div>
187
+ </div>
188
+ </div>
189
+
190
+ <div class="text-center mt-12">
191
+ <a href="#" class="inline-flex items-center text-amber-700 hover:text-amber-900 font-medium transition">
192
+ Vsi izdelki <i data-feather="chevron-right" class="ml-1"></i>
193
+ </a>
194
+ </div>
195
+ </div>
196
+ </section>
197
+
198
+ <!-- News Section -->
199
+ <section class="py-16 bg-amber-50">
200
+ <div class="container mx-auto px-4 flex flex-col md:flex-row items-center">
201
+ <div class="md:w-1/2 mb-8 md:mb-0" data-aos="fade-right">
202
+ <img src="https://static.photos/nature/640x360/105" alt="Nov čebelnjak" class="rounded-lg shadow-lg w-full">
203
+ </div>
204
+ <div class="md:w-1/2 md:pl-12" data-aos="fade-left">
205
+ <h2 class="text-3xl font-bold text-amber-900 mb-4">Čebelarstvo Cigoj gradi nov čebelnjak!</h2>
206
+ <p class="text-lg text-amber-800 mb-6">
207
+ Z veseljem sporočamo, da gradimo nov čebelnjak! Korak naprej za boljše pogoje za naše čebele in kakovostnejše pridelke iz domače narave.
208
+ </p>
209
+ <p class="text-amber-800 mb-6">
210
+ Hvala vsem, ki nas spremljate in podpirate.
211
+ </p>
212
+ <a href="#" class="text-amber-700 hover:text-amber-900 font-medium inline-flex items-center transition">
213
+ Preberite več <i data-feather="arrow-right" class="ml-2"></i>
214
+ </a>
215
+ </div>
216
+ </div>
217
+ </section>
218
+
219
+ <!-- Best Sellers -->
220
+ <section class="py-16 bg-white">
221
+ <div class="container mx-auto px-4">
222
+ <h2 class="text-3xl font-bold text-center text-amber-900 mb-12" data-aos="fade-up">Prodajne uspešnice</h2>
223
+
224
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8">
225
+ <!-- Product 1 -->
226
+ <div class="product-card bg-white rounded-lg overflow-hidden shadow-md relative transition duration-300" data-aos="fade-up" data-aos-delay="100">
227
+ <img src="https://static.photos/nature/320x240/106" alt="Balzam za ustnice" class="w-full h-48 object-cover">
228
+ <div class="p-4">
229
+ <h3 class="font-bold text-lg mb-2 text-amber-900">Balzam za ustnice iz čebeljega voska</h3>
230
+ <p class="text-amber-700 font-bold text-xl mb-3">2,50€</p>
231
+ <div class="flex justify-between">
232
+ <button class="bg-amber-100 hover:bg-amber-200 text-amber-800 px-3 py-1 rounded text-sm flex items-center transition">
233
+ <i data-feather="eye" class="mr-1 w-4 h-4"></i> Hitri ogled
234
+ </button>
235
+ <button class="bg-amber-600 hover:bg-amber-700 text-white px-3 py-1 rounded text-sm flex items-center transition">
236
+ <i data-feather="shopping-cart" class="mr-1 w-4 h-4"></i> V košarico
237
+ </button>
238
+ </div>
239
+ </div>
240
+ </div>
241
+
242
+ <!-- Product 2 -->
243
+ <div class="product-card bg-white rounded-lg overflow-hidden shadow-md relative transition duration-300" data-aos="fade-up" data-aos-delay="200">
244
+ <div class="sale-badge bg-red-500 text-white text-xs font-bold px-2 py-1 rounded-full">
245
+ RAZPRODAJA!
246
+ </div>
247
+ <img src="https://static.photos/nature/320x240/107" alt="Balzam za ustnice" class="w-full h-48 object-cover">
248
+ <div class="p-4">
249
+ <h3 class="font-bold text-lg mb-2 text-amber-900">Balzam za ustnice iz čebeljega voska</h3>
250
+ <p class="text-amber-700 font-bold text-xl mb-3">2,50€</p>
251
+ <div class="flex justify-between">
252
+ <button class="bg-amber-100 hover:bg-amber-200 text-amber-800 px-3 py-1 rounded text-sm flex items-center transition">
253
+ <i data-feather="eye" class="mr-1 w-4 h-4"></i> Hitri ogled
254
+ </button>
255
+ <button class="bg-amber-600 hover:bg-amber-700 text-white px-3 py-1 rounded text-sm flex items-center transition">
256
+ <i data-feather="shopping-cart" class="mr-1 w-4 h-4"></i> V košarico
257
+ </button>
258
+ </div>
259
+ </div>
260
+ </div>
261
+
262
+ <!-- Product 3 -->
263
+ <div class="product-card bg-white rounded-lg overflow-hidden shadow-md relative transition duration-300" data-aos="fade-up" data-aos-delay="300">
264
+ <div class="sale-badge bg-red-500 text-white text-xs font-bold px-2 py-1 rounded-full">
265
+ RAZPRODAJA!
266
+ </div>
267
+ <img src="https://static.photos/nature/320x240/108" alt="Cvetni prah 100g" class="w-full h-48 object-cover">
268
+ <div class="p-4">
269
+ <h3 class="font-bold text-lg mb-2 text-amber-900">Cvetni prah 100g</h3>
270
+ <p class="text-amber-700 font-bold text-xl mb-3">7,00€</p>
271
+ <div class="flex justify-between">
272
+ <button class="bg-amber-100 hover:bg-amber-200 text-amber-800 px-3 py-1 rounded text-sm flex items-center transition">
273
+ <i data-feather="eye" class="mr-1 w-4 h-4"></i> Hitri ogled
274
+ </button>
275
+ <button class="bg-amber-600 hover:bg-amber-700 text-white px-3 py-1 rounded text-sm flex items-center transition">
276
+ <i data-feather="shopping-cart" class="mr-1 w-4 h-4"></i> V košarico
277
+ </button>
278
+ </div>
279
+ </div>
280
+ </div>
281
+ </div>
282
+
283
+ <div class="text-center mt-12">
284
+ <a href="#" class="inline-flex items-center text-amber-700 hover:text-amber-900 font-medium transition">
285
+ Vse prodajne uspešnice <i data-feather="chevron-right" class="ml-1"></i>
286
+ </a>
287
+ </div>
288
+ </div>
289
+ </section>
290
+
291
+ <!-- Footer -->
292
+ <footer class="bg-amber-900 text-white py-12">
293
+ <div class="container mx-auto px-4">
294
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-8">
295
+ <div>
296
+ <h3 class="text-xl font-bold mb-4">Čebelarstvo Cigoj</h3>
297
+ <p class="text-amber-200">Kakovostni naravni izdelki iz čebeljega panja.</p>
298
+ </div>
299
+ <div>
300
+ <h4 class="font-bold mb-4">Izdelki</h4>
301
+ <ul class="space-y-2">
302
+ <li><a href="#" class="text-amber-200 hover:text-white transition">Med</a></li>
303
+ <li><a href="#" class="text-amber-200 hover:text-white transition">Cvetni prah</a></li>
304
+ <li><a href="#" class="text-amber-200 hover:text-white transition">Čebelji vosek</a></li>
305
+ <li><a href="#" class="text-amber-200 hover:text-white transition">Balzami</a></li>
306
+ </ul>
307
+ </div>
308
+ <div>
309
+ <h4 class="font-bold mb-4">Povezave</h4>
310
+ <ul class="space-y-2">
311
+ <li><a href="#" class="text-amber-200 hover:text-white transition">O nas</a></li>
312
+ <li><a href="#" class="text-amber-200 hover:text-white transition">Blog</a></li>
313
+ <li><a href="#" class="text-amber-200 hover:text-white transition">Pogoji poslovanja</a></li>
314
+ <li><a href="#" class="text-amber-200 hover:text-white transition">Varstvo zasebnosti</a></li>
315
+ <li><a href="backend.php" class="text-amber-200 hover:text-white transition">Administracija</a></li>
316
+ </ul>
317
+ </div>
318
+ <div>
319
+ <h4 class="font-bold mb-4">Kontakt</h4>
320
+ <ul class="space-y-2">
321
+ <li class="flex items-center">
322
+ <i data-feather="mail" class="mr-2"></i> info@cebelarstvo-cigoj.si
323
+ </li>
324
+ <li class="flex items-center">
325
+ <i data-feather="phone" class="mr-2"></i> +386 40 123 456
326
+ </li>
327
+ <li class="flex items-center">
328
+ <i data-feather="map-pin" class="mr-2"></i> Čebelarska ulica 1, 1000 Ljubljana
329
+ </li>
330
+ </ul>
331
+ </div>
332
+ </div>
333
+ <div class="border-t border-amber-800 mt-8 pt-8 text-center text-amber-200">
334
+ <p>&copy; 2023 Čebelarstvo Cigoj. Vse pravice pridržane.</p>
335
+ </div>
336
+ </div>
337
+ </footer>
338
+
339
+ <script>
340
+ // Cart functions
341
+ function addToCart(productId, productName, productPrice) {
342
+ let cart = JSON.parse(localStorage.getItem('cart')) || [];
343
+ let found = false;
344
+
345
+ cart.forEach(item => {
346
+ if(item.id === productId) {
347
+ item.quantity++;
348
+ found = true;
349
+ }
350
+ });
351
+
352
+ if(!found) {
353
+ cart.push({
354
+ id: productId,
355
+ name: productName,
356
+ price: productPrice,
357
+ quantity: 1
358
+ });
359
+ }
360
+
361
+ localStorage.setItem('cart', JSON.stringify(cart));
362
+ updateCartCount();
363
+ }
364
+
365
+ function updateCartCount() {
366
+ const cart = JSON.parse(localStorage.getItem('cart')) || [];
367
+ const count = cart.reduce((total, item) => total + item.quantity, 0);
368
+ const cartCount = document.getElementById('cart-count');
369
+
370
+ cartCount.textContent = count;
371
+ if(count > 0) {
372
+ cartCount.classList.remove('hidden');
373
+ } else {
374
+ cartCount.classList.add('hidden');
375
+ }
376
+ }
377
+
378
+ // Initialize cart count on page load
379
+ updateCartCount();
380
+
381
+ AOS.init({
382
+ duration: 800,
383
+ easing: 'ease-in-out',
384
+ once: true
385
+ });
386
+ feather.replace();
387
+ </script>
388
+ <script src="cart-fix.js"></script>
389
+ </body>
390
+ </html>
login.php ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once 'config.php';
3
+
4
+ // Only handle POST requests
5
+ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
6
+ http_response_code(405);
7
+ echo json_encode(['message' => 'Method not allowed']);
8
+ exit;
9
+ }
10
+
11
+ // Support both JSON (AJAX) and form-encoded POSTs. Prefer JSON when Content-Type is application/json.
12
+ $contentType = $_SERVER['CONTENT_TYPE'] ?? '';
13
+ if (stripos($contentType, 'application/json') !== false) {
14
+ $input = json_decode(file_get_contents('php://input'), true);
15
+ $username = htmlspecialchars(trim($input['username'] ?? ''), ENT_QUOTES, 'UTF-8');
16
+ $password = $input['password'] ?? '';
17
+ $is_form = false;
18
+ } else {
19
+ // form submission (application/x-www-form-urlencoded or multipart)
20
+ $username = htmlspecialchars(trim($_POST['username'] ?? ''), ENT_QUOTES, 'UTF-8');
21
+ $password = $_POST['password'] ?? '';
22
+ $is_form = true;
23
+ }
24
+
25
+ // Basic validation
26
+ if (empty($username) || empty($password)) {
27
+ if ($is_form) {
28
+ // Redirect back with error flag for simple form UX
29
+ header('Location: admin_login.php?error=1');
30
+ exit;
31
+ }
32
+ http_response_code(400);
33
+ echo json_encode(['message' => 'Username and password required']);
34
+ exit;
35
+ }
36
+
37
+ // Get client IP
38
+ $ip_address = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? 'unknown';
39
+
40
+ // Ensure login_attempts table exists (idempotent)
41
+ $pdo->exec(
42
+ "CREATE TABLE IF NOT EXISTS login_attempts (
43
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
44
+ email TEXT,
45
+ ip_address TEXT,
46
+ attempted_at DATETIME DEFAULT (datetime('now')),
47
+ success INTEGER DEFAULT 0
48
+ );"
49
+ );
50
+
51
+ // DEVELOPMENT: allow temporary bypass of rate-limiting by creating a file named
52
+ // DISABLE_RATE_LIMIT in the project root. This is handy when resetting or bootstrapping
53
+ // the admin account. Remove the file to re-enable rate limiting.
54
+ $bypass_rate_limit = false;
55
+ if (file_exists(__DIR__ . '/DISABLE_RATE_LIMIT')) {
56
+ $bypass_rate_limit = true;
57
+ }
58
+
59
+ // Check rate limits (5 attempts per email in 15 minutes, 10 attempts per IP in 15 minutes)
60
+ $now = date('Y-m-d H:i:s');
61
+ $fifteen_minutes_ago = date('Y-m-d H:i:s', strtotime('-15 minutes'));
62
+
63
+ // Check username rate limit (skip if bypass enabled)
64
+ if (!$bypass_rate_limit) {
65
+ $stmt = $pdo->prepare("SELECT COUNT(*) as attempts FROM login_attempts WHERE email = ? AND attempted_at > ? AND success = 0");
66
+ $stmt->execute([$username, $fifteen_minutes_ago]);
67
+ $username_attempts = $stmt->fetch()['attempts'];
68
+
69
+ if ($username_attempts >= 5) {
70
+ // Record failed attempt
71
+ $stmt = $pdo->prepare("INSERT INTO login_attempts (email, ip_address, success) VALUES (?, ?, 0)");
72
+ $stmt->execute([$username, $ip_address]);
73
+
74
+ http_response_code(429);
75
+ echo json_encode(['message' => 'Too many failed attempts for this username. Try again in 15 minutes.', 'retry_after' => 900]);
76
+ exit;
77
+ }
78
+ }
79
+
80
+ // Check IP rate limit (skip if bypass enabled)
81
+ if (!$bypass_rate_limit) {
82
+ $stmt = $pdo->prepare("SELECT COUNT(*) as attempts FROM login_attempts WHERE ip_address = ? AND attempted_at > ? AND success = 0");
83
+ $stmt->execute([$ip_address, $fifteen_minutes_ago]);
84
+ $ip_attempts = $stmt->fetch()['attempts'];
85
+
86
+ if ($ip_attempts >= 10) {
87
+ // Record failed attempt
88
+ $stmt = $pdo->prepare("INSERT INTO login_attempts (email, ip_address, success) VALUES (?, ?, 0)");
89
+ $stmt->execute([$username, $ip_address]);
90
+
91
+ http_response_code(429);
92
+ echo json_encode(['message' => 'Too many failed attempts from this IP. Try again in 15 minutes.', 'retry_after' => 900]);
93
+ exit;
94
+ }
95
+ }
96
+
97
+ // Find user (do not assume `is_active` column exists in older DB schema)
98
+ $stmt = $pdo->prepare("SELECT * FROM admin_users WHERE username = ?");
99
+ $stmt->execute([$username]);
100
+ $user = $stmt->fetch(PDO::FETCH_ASSOC);
101
+
102
+ // If the `is_active` column exists, enforce it; otherwise assume active by default
103
+ if ($user && isset($user['is_active']) && (int)$user['is_active'] !== 1) {
104
+ // Record failed attempt
105
+ $stmt = $pdo->prepare("INSERT INTO login_attempts (email, ip_address, success) VALUES (?, ?, 0)");
106
+ $stmt->execute([$username, $ip_address]);
107
+
108
+ http_response_code(401);
109
+ echo json_encode(['message' => 'Invalid credentials']);
110
+ exit;
111
+ }
112
+
113
+ if (!$user || !password_verify($password, $user['password_hash'])) {
114
+ // Record failed attempt
115
+ $stmt = $pdo->prepare("INSERT INTO login_attempts (email, ip_address, success) VALUES (?, ?, 0)");
116
+ $stmt->execute([$username, $ip_address]);
117
+
118
+ http_response_code(401);
119
+ echo json_encode(['message' => 'Invalid credentials']);
120
+ exit;
121
+ }
122
+
123
+ // Record successful attempt
124
+ $stmt = $pdo->prepare("INSERT INTO login_attempts (email, ip_address, success) VALUES (?, ?, 1)");
125
+ $stmt->execute([$username, $ip_address]);
126
+
127
+ // Generate JWT token (15 minutes expiry)
128
+ $payload = [
129
+ 'user_id' => $user['id'],
130
+ 'username' => $user['username'],
131
+ 'email' => $user['email'] ?? '',
132
+ 'role' => $user['role'] ?? 'admin',
133
+ 'iat' => time(),
134
+ 'exp' => time() + (15 * 60) // 15 minutes
135
+ ];
136
+
137
+ // Use fully-qualified class name to avoid file-local "use" dependency issues
138
+ $jwt = \Firebase\JWT\JWT::encode($payload, JWT_SECRET, 'HS256');
139
+
140
+ // Set secure cookie (for refresh token - simplified for demo)
141
+ // Choose cookie options depending on whether the request appears to be HTTPS.
142
+ // When served over HTTPS (e.g., GitHub.dev preview), use Secure + SameSite=None so the
143
+ // cookie can be accepted in some embedded contexts. For local HTTP development we keep
144
+ // Secure=false and SameSite=Strict.
145
+ $is_https = false;
146
+ if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') {
147
+ $is_https = true;
148
+ } elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
149
+ $is_https = true;
150
+ } elseif (!empty($_SERVER['HTTP_CF_VISITOR']) && strpos($_SERVER['HTTP_CF_VISITOR'], 'https') !== false) {
151
+ $is_https = true;
152
+ }
153
+
154
+ $cookie_options = [
155
+ 'expires' => time() + (7 * 24 * 60 * 60), // 7 days
156
+ 'path' => '/',
157
+ 'domain' => '',
158
+ 'secure' => $is_https, // require secure when over HTTPS
159
+ 'httponly' => true,
160
+ 'samesite' => $is_https ? 'None' : 'Strict'
161
+ ];
162
+
163
+ // PHP's setcookie accepted an array of options since 7.3; this usage is compatible.
164
+ setcookie('refresh_token', $jwt, $cookie_options);
165
+
166
+ if ($is_form) {
167
+ // For browser form submissions, instead of a bare 302 (which may be followed inside an iframe
168
+ // or blocked by preview environments), return a small HTML page that attempts a top-level
169
+ // navigation. The Set-Cookie header has already been emitted above so the cookie will apply.
170
+ header('Content-Type: text/html; charset=utf-8');
171
+ $dashboard = 'backend.php';
172
+ echo "<!doctype html>\n";
173
+ echo "<html><head><meta charset=\"utf-8\"><title>Prijava uspešna</title>\n";
174
+ // Meta refresh as an extra fallback
175
+ echo "<meta http-equiv=\"refresh\" content=\"0;url={$dashboard}\">\n";
176
+ echo "</head><body>\n";
177
+ echo "<script>\n";
178
+ echo "try {\n";
179
+ echo " if (window.top && window.top !== window) {\n";
180
+ echo " window.top.location.replace('{$dashboard}');\n";
181
+ echo " } else {\n";
182
+ echo " window.location.replace('{$dashboard}');\n";
183
+ echo " }\n";
184
+ echo "} catch (e) {\n";
185
+ echo " try { window.location.replace('{$dashboard}'); } catch (e) { /* ignore */ }\n";
186
+ echo "}\n";
187
+ echo "</script>\n";
188
+ echo "<p>Prijava uspešna. Če se brskalnik ne preusmeri samodejno, <a href=\"{$dashboard}\">kliknite tukaj</a>.</p>\n";
189
+ // Large fallback button to open dashboard in a new tab or top window
190
+ echo "<div style=\"margin-top:16px;\">\n";
191
+ echo " <a href=\"{$dashboard}\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"display:inline-block;background:#f59e0b;color:#fff;padding:8px 12px;border-radius:6px;text-decoration:none\">Open dashboard in new tab</a>\n";
192
+ echo " <button id=\"openTopBtn\" style=\"margin-left:8px;padding:8px 12px;border-radius:6px;background:#ef4444;color:#fff;border:none;cursor:pointer\">Open dashboard (top)</button>\n";
193
+ echo "</div>\n";
194
+ echo "<script>document.getElementById('openTopBtn').addEventListener('click', function(){ try{ if(window.top && window.top !== window){ window.top.location.href='${dashboard}'; } else { window.location.href='${dashboard}'; } }catch(e){ window.open('${dashboard}','_blank'); } });</script>\n";
195
+ echo "</body></html>\n";
196
+ exit;
197
+ }
198
+
199
+ // Default: JSON response for API/AJAX clients
200
+ echo json_encode([
201
+ 'message' => 'Login successful',
202
+ 'token' => $jwt,
203
+ 'expires_in' => 900, // 15 minutes
204
+ 'user' => [
205
+ 'id' => $user['id'],
206
+ 'username' => $user['username'],
207
+ 'email' => $user['email'] ?? '',
208
+ 'role' => $user['role'] ?? 'admin'
209
+ ]
210
+ ]);
211
+ ?>
logout.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Securely log out admin by clearing the refresh_token cookie and redirecting to login
3
+ setcookie('refresh_token', '', [
4
+ 'expires' => time() - 3600,
5
+ 'path' => '/',
6
+ 'domain' => '',
7
+ 'secure' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'),
8
+ 'httponly' => true,
9
+ 'samesite' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'None' : 'Strict'
10
+ ]);
11
+ header('Location: admin_login.php');
12
+ exit;
orders.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once 'config.php';
3
+
4
+ // GET all orders (Admin only)
5
+ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
6
+ require_once 'require_auth.php'; // This now includes admin role checking
7
+ $accept = $_SERVER['HTTP_ACCEPT'] ?? '';
8
+ $is_html = strpos($accept, 'text/html') !== false || isset($_GET['id']);
9
+ $order_id = $_GET['id'] ?? null;
10
+ if ($is_html && $order_id) {
11
+ // Render HTML order details page
12
+ $stmt = $pdo->prepare("SELECT * FROM orders WHERE id = ?");
13
+ $stmt->execute([$order_id]);
14
+ $order = $stmt->fetch(PDO::FETCH_ASSOC);
15
+ if (!$order) {
16
+ echo "<h2 style='color:red'>Order not found.</h2>";
17
+ exit;
18
+ }
19
+ $stmt = $pdo->prepare("SELECT * FROM order_items WHERE order_id = ?");
20
+ $stmt->execute([$order_id]);
21
+ $items = $stmt->fetchAll(PDO::FETCH_ASSOC);
22
+ echo "<!DOCTYPE html><html lang='sl'><head><meta charset='utf-8'><title>Podrobnosti naročila #{$order_id}</title><script src='https://cdn.tailwindcss.com'></script></head><body class='bg-amber-50'>";
23
+ echo "<div class='max-w-2xl mx-auto my-8 bg-white rounded-lg shadow p-6'>";
24
+ echo "<h1 class='text-2xl font-bold mb-4 text-amber-900'>Naročilo #{$order_id}</h1>";
25
+ echo "<div class='mb-4'><strong>Stranka:</strong> {$order['customer_name']}<br><strong>Email:</strong> {$order['customer_email']}<br><strong>Naslov:</strong> {$order['customer_address']}<br><strong>Telefon:</strong> {$order['customer_phone']}</div>";
26
+ echo "<div class='mb-4'><strong>Status:</strong> {$order['status']}<br><strong>Skupaj:</strong> {$order['total_amount']} €<br><strong>Datum:</strong> {$order['created_at']}</div>";
27
+ echo "<h2 class='text-xl font-semibold mb-2 text-amber-800'>Izdelki</h2>";
28
+ echo "<table class='w-full border'><thead><tr class='bg-amber-100'><th class='p-2 text-left'>Izdelek</th><th class='p-2 text-right'>Količina</th><th class='p-2 text-right'>Cena</th></tr></thead><tbody>";
29
+ foreach ($items as $item) {
30
+ echo "<tr><td class='p-2'>{$item['product_name']}</td><td class='p-2 text-right'>{$item['quantity']}</td><td class='p-2 text-right'>{$item['total_price']} €</td></tr>";
31
+ }
32
+ echo "</tbody></table>";
33
+ echo "<div class='mt-6'><a href='backend.php' class='bg-amber-700 text-white px-4 py-2 rounded'>Nazaj na nadzorno ploščo</a></div>";
34
+ echo "</div></body></html>";
35
+ exit;
36
+ }
37
+ // Default: return all orders as JSON (API)
38
+ $stmt = $pdo->query("SELECT * FROM orders");
39
+ $orders = $stmt->fetchAll(PDO::FETCH_ASSOC);
40
+ foreach($orders as &$order) {
41
+ $stmt = $pdo->prepare("SELECT * FROM order_items WHERE order_id = ?");
42
+ $stmt->execute([$order['id']]);
43
+ $order['items'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
44
+ }
45
+ header('Content-Type: application/json; charset=utf-8');
46
+ echo json_encode($orders);
47
+ }
48
+
49
+ // POST create new order
50
+ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
51
+ $data = json_decode(file_get_contents("php://input"));
52
+
53
+ // Start transaction
54
+ $pdo->beginTransaction();
55
+
56
+ try {
57
+ // Create order
58
+ $stmt = $pdo->prepare("INSERT INTO orders (customer_name, customer_email, customer_address, total_amount, status) VALUES (?, ?, ?, ?, ?)");
59
+ $stmt->execute([
60
+ $data->customer->name,
61
+ $data->customer->email,
62
+ $data->customer->address,
63
+ $data->total,
64
+ 'pending'
65
+ ]);
66
+ $order_id = $pdo->lastInsertId();
67
+
68
+ // Add order items
69
+ foreach($data->items as $item) {
70
+ $stmt = $pdo->prepare("INSERT INTO order_items (order_id, product_id, product_name, quantity, unit_price) VALUES (?, ?, ?, ?, ?)");
71
+ $stmt->execute([
72
+ $order_id,
73
+ $item->product_id,
74
+ $item->product_name,
75
+ $item->quantity,
76
+ $item->unit_price
77
+ ]);
78
+
79
+ // Update product stock
80
+ $stmt = $pdo->prepare("UPDATE products SET stock = stock - ? WHERE id = ?");
81
+ $stmt->execute([$item->quantity, $item->product_id]);
82
+ }
83
+
84
+ // Commit transaction
85
+ $pdo->commit();
86
+
87
+ http_response_code(201);
88
+ echo json_encode(array("success" => true, "order_id" => $order_id));
89
+ } catch(Exception $e) {
90
+ // Rollback on error
91
+ $pdo->rollBack();
92
+ http_response_code(500);
93
+ echo json_encode(array("success" => false, "message" => $e->getMessage()));
94
+ }
95
+ }
96
+
97
+ // UPDATE order status (Admin only)
98
+ if ($_SERVER['REQUEST_METHOD'] === 'PUT') {
99
+ require_once 'require_auth.php'; // This now includes admin role checking
100
+
101
+ $data = json_decode(file_get_contents("php://input"));
102
+ $id = $_GET['id'] ?? null;
103
+
104
+ if($id) {
105
+ $stmt = $pdo->prepare("UPDATE orders SET status = ? WHERE id = ?");
106
+ $stmt->execute([$data->status, $id]);
107
+
108
+ echo json_encode(array("success" => true, "message" => "Order updated"));
109
+ } else {
110
+ http_response_code(400);
111
+ echo json_encode(array("success" => false, "message" => "Order ID required"));
112
+ }
113
+ }
114
+ ?>
process_order.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ```php
2
+ <?php
3
+ require_once 'config.php';
4
+
5
+ // Get cart from session or cookie
6
+ $cart = isset($_SESSION['cart']) ? $_SESSION['cart'] : (isset($_COOKIE['cart']) ? json_decode($_COOKIE['cart'], true) : []);
7
+
8
+ // Calculate total
9
+ $total = 0;
10
+ foreach($cart as $item) {
11
+ $total += $item['price'] * $item['quantity'];
12
+ }
13
+
14
+ // Handle form submission
15
+ if($_SERVER['REQUEST_METHOD'] === 'POST') {
16
+ $customer = [
17
+ 'name' => $_POST['name'],
18
+ 'email' => $_POST['email'],
19
+ 'address' => $_POST['address'],
20
+ 'phone' => $_POST['phone']
21
+ ];
22
+
23
+ // DEBUG: Output cart contents for troubleshooting
24
+ file_put_contents('cart_debug.log', print_r($cart, true));
25
+
26
+ // Prepare order data
27
+ $order_data = [
28
+ 'customer' => $customer,
29
+ 'items' => $cart,
30
+ 'total' => $total + 5 // Including shipping
31
+ ];
32
+
33
+ // Create order (this would be a call to your orders.php API in a real app)
34
+ $order_id = createOrder($order_data['customer'], $order_data['total'], $cart);
35
+
36
+ // Clear cart
37
+ unset($_SESSION['cart']);
38
+ setcookie('cart', '', time() - 3600, '/');
39
+
40
+ // Redirect to thank you page
41
+ header('Location: thank_you.php?order_id=' . $order_id);
42
+ exit;
43
+ }
44
+
45
+ // Function to create order (simplified for example)
46
+ function createOrder($customer, $total, $items) {
47
+ global $pdo;
48
+ // Insert order with customer_phone
49
+ $stmt = $pdo->prepare("INSERT INTO orders (customer_name, customer_email, customer_address, customer_phone, total_amount) VALUES (?, ?, ?, ?, ?)");
50
+ $stmt->execute([$customer['name'], $customer['email'], $customer['address'], $customer['phone'], $total]);
51
+ $order_id = $pdo->lastInsertId();
52
+
53
+ // Insert order items
54
+ foreach($items as $item) {
55
+ if (!isset($item['id']) || empty($item['id'])) {
56
+ // Skip items without a valid product_id
57
+ continue;
58
+ }
59
+ $stmt = $pdo->prepare("INSERT INTO order_items (order_id, product_id, product_name, quantity, unit_price) VALUES (?, ?, ?, ?, ?)");
60
+ $stmt->execute([$order_id, $item['id'], $item['name'], $item['quantity'], $item['price']]);
61
+
62
+ // Update product stock
63
+ $stmt = $pdo->prepare("UPDATE products SET stock = stock - ? WHERE id = ?");
64
+ $stmt->execute([$item['quantity'], $item['id']]);
65
+ }
66
+
67
+ return $order_id;
68
+ }
69
+ ?>
70
+ ```
products.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once 'config.php';
3
+
4
+ // GET all products
5
+ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
6
+ $stmt = $pdo->query("SELECT * FROM products");
7
+ $products = $stmt->fetchAll(PDO::FETCH_ASSOC);
8
+ echo json_encode($products);
9
+ }
10
+
11
+ // POST new product (Admin only)
12
+ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
13
+ require_once 'require_auth.php'; // This now includes admin role checking
14
+
15
+ $data = json_decode(file_get_contents("php://input"));
16
+
17
+ $stmt = $pdo->prepare("INSERT INTO products (name, description, price, stock, image_url) VALUES (?, ?, ?, ?, ?)");
18
+ $stmt->execute([$data->name, $data->description, $data->price, $data->stock, $data->image_url]);
19
+
20
+ $product_id = $pdo->lastInsertId();
21
+ $stmt = $pdo->query("SELECT * FROM products WHERE id = $product_id");
22
+ $product = $stmt->fetch(PDO::FETCH_ASSOC);
23
+
24
+ echo json_encode($product);
25
+ }
26
+
27
+ // UPDATE product (Admin only)
28
+ if ($_SERVER['REQUEST_METHOD'] === 'PUT') {
29
+ require_once 'require_auth.php'; // This now includes admin role checking
30
+
31
+ $data = json_decode(file_get_contents("php://input"));
32
+ $id = $_GET['id'] ?? null;
33
+
34
+ if($id) {
35
+ $stmt = $pdo->prepare("UPDATE products SET name = ?, description = ?, price = ?, stock = ?, image_url = ? WHERE id = ?");
36
+ $stmt->execute([$data->name, $data->description, $data->price, $data->stock, $data->image_url, $id]);
37
+
38
+ echo json_encode(array("success" => true, "message" => "Product updated"));
39
+ } else {
40
+ http_response_code(400);
41
+ echo json_encode(array("success" => false, "message" => "Product ID required"));
42
+ }
43
+ }
44
+
45
+ // DELETE product (Admin only)
46
+ if ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
47
+ require_once 'require_auth.php'; // This now includes admin role checking
48
+
49
+ $id = $_GET['id'] ?? null;
50
+
51
+ if($id) {
52
+ $stmt = $pdo->prepare("DELETE FROM products WHERE id = ?");
53
+ $stmt->execute([$id]);
54
+
55
+ echo json_encode(array("success" => true, "message" => "Product deleted"));
56
+ } else {
57
+ http_response_code(400);
58
+ echo json_encode(array("success" => false, "message" => "Product ID required"));
59
+ }
60
+ }
61
+ ?>
prompts.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ now make backend
3
+ now make the php fort it to work safely
4
+ now make the php fort it to work safely
5
+ now make the php fort it to work safely
6
+ make the backend so the user can anctualy cehck pout
7
+ make all the backend phps and
8
+ make the backend php
9
+ link it so when the user checkouts it actulay gets redirected to checkout
10
+ fix the main page its not shjowing coreclty
replit.md ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Overview
2
+
3
+ This is a Slovenian beekeeping e-commerce website called "Čebelarstvo Cigoj" (Cigoj Beekeeping). The project is a honey and beekeeping products online store with both customer-facing pages and administrative functionality. It features a modern, responsive design using Tailwind CSS with an amber/brown color scheme that reflects the beekeeping theme.
4
+
5
+ The application includes a product catalog, shopping cart functionality, and an administrative backend for managing products and orders. The site is designed to sell premium natural honey and beekeeping products to customers while providing business owners with tools to manage their inventory and sales.
6
+
7
+ # User Preferences
8
+
9
+ Preferred communication style: Simple, everyday language.
10
+
11
+ # System Architecture
12
+
13
+ ## Frontend Architecture
14
+ The frontend is built as a multi-page static HTML application using:
15
+ - **Tailwind CSS** for responsive styling and component design
16
+ - **Feather Icons** for consistent iconography throughout the interface
17
+ - **AOS (Animate On Scroll)** library for smooth scroll animations
18
+ - Vanilla JavaScript for client-side interactivity and cart management
19
+
20
+ The design follows a consistent amber/brown color palette that reflects the beekeeping theme, with the primary brand color being `amber-800`. The layout is fully responsive and includes:
21
+ - Main storefront page (`index.html`)
22
+ - Shopping cart page (`cart.html`)
23
+ - Administrative dashboard (`backend.html`)
24
+
25
+ ## Backend Architecture
26
+ The backend is designed to be implemented in PHP with:
27
+ - **Composer** dependency management
28
+ - **Firebase JWT** library for secure authentication and session management
29
+ - Server-side cart processing and checkout functionality
30
+ - Administrative controls for product and order management
31
+
32
+ The architecture separates customer-facing functionality from administrative features, with the admin panel requiring authentication.
33
+
34
+ ## Authentication System
35
+ Authentication is handled using JWT tokens via the Firebase PHP-JWT library, providing:
36
+ - Secure login sessions for administrative access
37
+ - Token-based authentication for API endpoints
38
+ - Session management for cart persistence
39
+
40
+ ## Cart and Checkout System
41
+ The shopping cart system uses:
42
+ - Client-side JavaScript for immediate cart updates
43
+ - Server-side PHP processing for secure checkout operations
44
+ - Session storage for cart persistence across page loads
45
+ - Integration with payment processing (to be implemented)
46
+
47
+ ## Administrative Features
48
+ The admin backend provides:
49
+ - Product inventory management
50
+ - Order processing and tracking
51
+ - Dashboard with business analytics
52
+ - Secure authentication-protected access
53
+
54
+ The admin interface uses the same design system as the customer-facing pages but with additional functionality for business management.
55
+
56
+ # External Dependencies
57
+
58
+ ## Frontend Libraries
59
+ - **Tailwind CSS CDN** - Utility-first CSS framework for responsive design
60
+ - **Feather Icons CDN** - Lightweight icon library for consistent UI elements
61
+ - **AOS (Animate On Scroll) CDN** - Animation library for scroll-triggered effects
62
+
63
+ ## Backend Dependencies
64
+ - **Firebase PHP-JWT** (v6.11.1) - JWT token creation and validation for secure authentication
65
+ - **PHP 8.0+** - Server-side language requirement for JWT library compatibility
66
+
67
+ ## Development Tools
68
+ - **Composer** - PHP dependency management for backend packages
69
+
70
+ The application is designed to be lightweight and fast-loading by leveraging CDN resources for frontend libraries while maintaining secure server-side processing for sensitive operations like authentication and payment processing.
reports.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ```php
2
+ <?php
3
+ require_once 'config.php';
4
+
5
+ // JWT verification middleware
6
+ verifyToken();
7
+
8
+ // GET sales report
9
+ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
10
+ $start_date = $_GET['start_date'] ?? date('Y-m-01');
11
+ $end_date = $_GET['end_date'] ?? date('Y-m-t');
12
+
13
+ // Total sales
14
+ $stmt = $pdo->prepare("SELECT SUM(total_amount) as total_sales FROM orders WHERE created_at BETWEEN ? AND ?");
15
+ $stmt->execute([$start_date, $end_date]);
16
+ $total_sales = $stmt->fetch(PDO::FETCH_ASSOC);
17
+
18
+ // Top products
19
+ $stmt = $pdo->prepare("
20
+ SELECT p.name, SUM(oi.quantity) as total_sold, SUM(oi.quantity * oi.unit_price) as revenue
21
+ FROM order_items oi
22
+ JOIN products p ON oi.product_id = p.id
23
+ JOIN orders o ON oi.order_id = o.id
24
+ WHERE o.created_at BETWEEN ? AND ?
25
+ GROUP BY p.id
26
+ ORDER BY total_sold DESC
27
+ LIMIT 5
28
+ ");
29
+ $stmt->execute([$start_date, $end_date]);
30
+ $top_products = $stmt->fetchAll(PDO::FETCH_ASSOC);
31
+
32
+ // Report data
33
+ $report = array(
34
+ 'total_sales' => $total_sales['total_sales'] ?? 0,
35
+ 'start_date' => $start_date,
36
+ 'end_date' => $end_date,
37
+ 'top_products' => $top_products
38
+ );
39
+
40
+ echo json_encode($report);
41
+ }
42
+ ?>
43
+ ```
44
+
45
+ You'll also need to create the database tables. Here's the SQL schema:
require_auth.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Authentication middleware - include at the top of protected pages
3
+ require_once 'config.php';
4
+
5
+ function requireAuth($required_role = null) {
6
+ // Check for JWT token in Authorization header or cookie
7
+ $token = null;
8
+
9
+ // First try Authorization header
10
+ $headers = function_exists('getallheaders') ? getallheaders() : [];
11
+ if (empty($headers) && isset($_SERVER['HTTP_AUTHORIZATION'])) {
12
+ $headers['Authorization'] = $_SERVER['HTTP_AUTHORIZATION'];
13
+ }
14
+
15
+ if (isset($headers['Authorization'])) {
16
+ $token = str_replace('Bearer ', '', $headers['Authorization']);
17
+ }
18
+ // Fallback to cookie
19
+ elseif (isset($_COOKIE['refresh_token'])) {
20
+ $token = $_COOKIE['refresh_token'];
21
+ }
22
+
23
+ if (!$token) {
24
+ // If the client expects HTML (browser navigation), redirect to login page
25
+ $accept = $_SERVER['HTTP_ACCEPT'] ?? '';
26
+ $isBrowser = strpos($accept, 'text/html') !== false || (isset($_SERVER['REQUEST_URI']) && (strpos($_SERVER['REQUEST_URI'], '.php') !== false));
27
+ if ($isBrowser) {
28
+ header('Location: /admin_login.php');
29
+ exit;
30
+ }
31
+
32
+ http_response_code(401);
33
+ echo json_encode(['message' => 'Authentication required']);
34
+ exit;
35
+ }
36
+
37
+ try {
38
+ // Use fully-qualified class names to ensure the Firebase JWT classes are found
39
+ $decoded = \Firebase\JWT\JWT::decode($token, new \Firebase\JWT\Key(JWT_SECRET, 'HS256'));
40
+
41
+ // Verify user still exists and is active (tolerant of missing is_active column)
42
+ global $pdo;
43
+ $stmt = $pdo->prepare("SELECT * FROM admin_users WHERE id = ?");
44
+ $stmt->execute([$decoded->user_id]);
45
+ $user = $stmt->fetch();
46
+ // If is_active column exists, enforce it; otherwise assume active
47
+ $inactive = false;
48
+ if ($user && array_key_exists('is_active', $user) && (int)$user['is_active'] !== 1) {
49
+ $inactive = true;
50
+ }
51
+ if (!$user || $inactive) {
52
+ $accept = $_SERVER['HTTP_ACCEPT'] ?? '';
53
+ $isBrowser = strpos($accept, 'text/html') !== false || (isset($_SERVER['REQUEST_URI']) && (strpos($_SERVER['REQUEST_URI'], '.php') !== false));
54
+ if ($isBrowser) {
55
+ header('Content-Type: text/html; charset=utf-8');
56
+ echo "<h2 style='color:red'>Admin user not found or inactive. Please check your database.</h2>";
57
+ exit;
58
+ }
59
+ http_response_code(401);
60
+ echo json_encode(['message' => 'User not found or inactive']);
61
+ exit;
62
+ }
63
+
64
+ // Check role if specified
65
+ if ($required_role && (!isset($user['role']) || $user['role'] !== $required_role)) {
66
+ http_response_code(403);
67
+ echo json_encode(['message' => 'Insufficient permissions']);
68
+ exit;
69
+ }
70
+
71
+ // Set global user data
72
+ global $current_user;
73
+ $current_user = $user;
74
+
75
+ return $decoded;
76
+
77
+ } catch (Exception $e) {
78
+ $accept = $_SERVER['HTTP_ACCEPT'] ?? '';
79
+ $isBrowser = strpos($accept, 'text/html') !== false || (isset($_SERVER['REQUEST_URI']) && (strpos($_SERVER['REQUEST_URI'], '.php') !== false));
80
+ if ($isBrowser) {
81
+ header('Location: /admin_login.php');
82
+ exit;
83
+ }
84
+
85
+ http_response_code(401);
86
+ echo json_encode(['message' => 'Invalid or expired token', 'error' => $e->getMessage()]);
87
+ exit;
88
+ }
89
+ }
90
+
91
+ // Call requireAuth() to protect the current page with admin role
92
+ requireAuth('admin');
93
+ ?>
schema.sql ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -- SQLite schema
2
+ -- Products table
3
+ CREATE TABLE IF NOT EXISTS products (
4
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
5
+ name TEXT NOT NULL,
6
+ description TEXT,
7
+ price REAL NOT NULL,
8
+ stock INTEGER NOT NULL DEFAULT 0,
9
+ image_url TEXT,
10
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
11
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
12
+ );
13
+
14
+ -- Customers table
15
+ CREATE TABLE IF NOT EXISTS customers (
16
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
17
+ name TEXT NOT NULL,
18
+ email TEXT NOT NULL UNIQUE,
19
+ address TEXT,
20
+ phone TEXT,
21
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
22
+ );
23
+
24
+ -- Orders table
25
+ CREATE TABLE IF NOT EXISTS orders (
26
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
27
+ customer_name TEXT NOT NULL,
28
+ customer_email TEXT NOT NULL,
29
+ customer_address TEXT NOT NULL,
30
+ customer_phone TEXT,
31
+ subtotal REAL,
32
+ shipping_cost REAL,
33
+ shipping_method TEXT,
34
+ total_amount REAL NOT NULL,
35
+ status TEXT DEFAULT 'pending',
36
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
37
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
38
+ );
39
+
40
+ -- Order items table
41
+ CREATE TABLE IF NOT EXISTS order_items (
42
+ id INT AUTO_INCREMENT PRIMARY KEY,
43
+ order_id INT NOT NULL,
44
+
45
+ product_id INTEGER NOT NULL,
46
+ product_name TEXT NOT NULL,
47
+ quantity INTEGER NOT NULL,
48
+ unit_price REAL NOT NULL,
49
+ total_price REAL,
50
+ FOREIGN KEY (order_id) REFERENCES orders(id),
51
+ FOREIGN KEY (product_id) REFERENCES products(id)
52
+ );
53
+
54
+ -- Admin users table
55
+ CREATE TABLE IF NOT EXISTS admin_users (
56
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
57
+ username TEXT NOT NULL UNIQUE,
58
+ password_hash TEXT NOT NULL,
59
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
60
+ );
61
+
62
+ -- Insert test admin
63
+ INSERT OR IGNORE INTO admin_users (username, password_hash)
64
+ VALUES ('admin', '$2y$10$J4o8/.jGJt4vB7R7hNQ0L.Q5Qd8RZ1Z1d5Gz0V1W5gX6t5i2b1V1S2');
65
+
66
+ -- Insert sample products
67
+ INSERT OR IGNORE INTO products (name, description, price, stock, image_url) VALUES
68
+ ('Cvetni prah 50g', 'Naravni cvetni prah, bogat s proteini in vitamini', 5.00, 100, 'https://static.photos/nature/320x240/101'),
69
+ ('Balzam za ustnice iz čebeljega voska', 'Neguje in ščiti ustnice', 2.50, 50, 'https://static.photos/nature/320x240/102'),
70
+ ('Med ajdov', 'Visokokakovosten med iz ajdovega cveta', 7.50, 30, 'https://static.photos/nature/320x240/103');
style.css CHANGED
@@ -1,28 +1,28 @@
1
- body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
4
- }
5
-
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
9
- }
10
-
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
- }
17
-
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
- }
25
-
26
- .card p:last-child {
27
- margin-bottom: 0;
28
- }
 
1
+ body {
2
+ padding: 2rem;
3
+ font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
4
+ }
5
+
6
+ h1 {
7
+ font-size: 16px;
8
+ margin-top: 0;
9
+ }
10
+
11
+ p {
12
+ color: rgb(107, 114, 128);
13
+ font-size: 15px;
14
+ margin-bottom: 10px;
15
+ margin-top: 5px;
16
+ }
17
+
18
+ .card {
19
+ max-width: 620px;
20
+ margin: 0 auto;
21
+ padding: 16px;
22
+ border: 1px solid lightgray;
23
+ border-radius: 16px;
24
+ }
25
+
26
+ .card p:last-child {
27
+ margin-bottom: 0;
28
+ }
thank_you.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ```php
2
+ <?php
3
+ require_once 'config.php';
4
+
5
+ $order_id = $_GET['order_id'] ?? 0;
6
+ ?>
7
+ <!DOCTYPE html>
8
+ <html lang="sl">
9
+ <head>
10
+ <meta charset="UTF-8">
11
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
12
+ <title>Hvala za naročilo | Čebelarstvo Cigoj</title>
13
+ <script src="https://cdn.tailwindcss.com"></script>
14
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
15
+ </head>
16
+ <body class="font-sans bg-gray-50">
17
+ <header class="bg-amber-800 text-white shadow-md">
18
+ <div class="container mx-auto px-4 py-4">
19
+ <h1 class="text-xl font-bold">Čebelarstvo Cigoj</h1>
20
+ </div>
21
+ </header>
22
+
23
+ <main class="container mx-auto px-4 py-12">
24
+ <div class="max-w-lg mx-auto text-center bg-white rounded-lg shadow p-8">
25
+ <div class="text-green-500 mb-4">
26
+ <i data-feather="check-circle" class="w-16 h-16 mx-auto"></i>
27
+ </div>
28
+ <h2 class="text-2xl font-bold text-amber-900 mb-4">Hvala za vaše naročilo!</h2>
29
+ <p class="text-gray-600 mb-6">Vaše naročilo številka <span class="font-bold">#<?php echo $order_id; ?></span> je bilo uspešno oddano.</p>
30
+ <p class="text-gray-600 mb-6">Potrdilo naročila smo vam poslali na vaš e-mail naslov.</p>
31
+ <a href="/" class="inline-block bg-amber-600 hover:bg-amber-700 text-white px-6 py-3 rounded-lg font-medium transition">
32
+ Nazaj v trgovino
33
+ </a>
34
+ </div>
35
+ </main>
36
+
37
+ <script>
38
+ feather.replace();
39
+ </script>
40
+ </body>
41
+ </html>
42
+ ```
43
+
44
+ These changes add:
45
+ 1. Cart functionality using localStorage
46
+ 2. Checkout page with order summary
47
+ 3. Order processing page
48
+ 4. Thank you page after successful order
49
+ 5. Complete order flow from cart to confirmation
50
+
51
+ To fully implement this, make sure your orders.php API endpoint is properly set up to handle the order creation. You might also want to add:
52
+ - Payment gateway integration (like Stripe or PayPal)
53
+ - Email notifications
54
+ - Order status tracking
55
+ - Better validation in the checkout form
tmp_update_admin_password (1).php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $db = __DIR__ . '/cebelarstvo_cigoj.db';
3
+ $pdo = new PDO('sqlite:' . $db);
4
+ $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
5
+ $hash = '$2y$10$B8XjImKVuSlqv60dOG7z7eiFXY12v4m0Nf5pfVVcwpFGpeTxB8R8C';
6
+ $stmt = $pdo->prepare('UPDATE admin_users SET password_hash = ? WHERE username = ?');
7
+ $stmt->execute([$hash, 'admin']);
8
+ $row = $pdo->query("SELECT id, username, password_hash FROM admin_users WHERE username='admin'")->fetch(PDO::FETCH_ASSOC);
9
+ print_r($row);
10
+ echo "\n";
11
+ ?>
tmp_update_admin_password.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Update admin password hash safely using the correct DB path
3
+ $db = __DIR__ . '/cebelarstvo_cigoj.db';
4
+ $pdo = new PDO('sqlite:' . $db);
5
+ $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
6
+ $hash = '$2y$10$B8XjImKVuSlqv60dOG7z7eiFXY12v4m0Nf5pfVVcwpFGpeTxB8R8C';
7
+ $stmt = $pdo->prepare('UPDATE admin_users SET password_hash = ? WHERE username = ?');
8
+ $stmt->execute([$hash, 'admin']);
9
+ $row = $pdo->query("SELECT id, username, password_hash FROM admin_users WHERE username='admin'")->fetch(PDO::FETCH_ASSOC);
10
+ print_r($row);
11
+ // Verify password locally
12
+ var_export(password_verify('secret123', $row['password_hash']));
13
+ echo "\n";
14
+ ?>