source: interfejs/store-owner.html@ 69f2a41

finki-main main
Last change on this file since 69f2a41 was 69f2a41, checked in by Klimentina Efremova <klimentina08642@…>, 7 months ago

Initial commit

  • Property mode set to 100644
File size: 41.0 KB
RevLine 
[69f2a41]1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Store Owner Dashboard - Handcraft Marketplace</title>
7 <link rel="stylesheet" href="style.css">
8</head>
9<body>
10<header>
11 <div class="logo-container">
12 <img src="../images/administrative/logo.jpg" alt="Handcraft Marketplace Logo" onerror="this.style.display='none'">
13 <h1>🎨 Handcraft Marketplace</h1>
14 </div>
15 <nav>
16 <ul>
17 <li><a href="index.html">Home</a></li>
18 <li><a href="products.html">Products</a></li>
19 <li><a href="#" id="dashboard-link" class="active">Store Owner</a></li>
20 </ul>
21 </nav>
22</header>
23
24<main>
25 <div class="dashboard-header">
26 <h2>Store Owner Dashboard</h2>
27 <p>Welcome, <span id="owner-name">Store Owner</span>!</p>
28 </div>
29
30 <div class="form-group">
31 <label for="store-select">Select Store:</label>
32 <select id="store-select" class="store-selector">
33 <option value="">Loading stores...</option>
34 </select>
35 </div>
36
37 <div id="store-dashboard" style="display: none;">
38 <div class="dashboard-stats">
39 <div class="stat-card pink">
40 <h3>Total Products</h3>
41 <div class="stat-number" id="total-products">0</div>
42 </div>
43 <div class="stat-card blue">
44 <h3>Total Orders</h3>
45 <div class="stat-number" id="total-orders">0</div>
46 </div>
47 <div class="stat-card">
48 <h3>Total Revenue</h3>
49 <div class="stat-number" id="total-revenue">$0</div>
50 </div>
51 <div class="stat-card pink">
52 <h3>Avg Rating</h3>
53 <div class="stat-number" id="avg-rating">0.0</div>
54 </div>
55 </div>
56
57 <div class="tabs">
58 <button class="tab-btn active" data-tab="products">Products</button>
59 <button class="tab-btn" data-tab="orders">Orders</button>
60 <button class="tab-btn" data-tab="employees">Employees</button>
61 <button class="tab-btn" data-tab="reports">Reports</button>
62 <button class="tab-btn" data-tab="add-product">Add Product</button>
63 <button class="tab-btn" data-tab="add-employee">Add Employee</button>
64 </div>
65
66 <!-- Products Tab -->
67 <div id="products-tab" class="tab-content active">
68 <div class="dashboard-section">
69 <h3>Store Products</h3>
70 <div class="table-responsive">
71 <table id="products-table">
72 <thead>
73 <tr>
74 <th>Code</th>
75 <th>Description</th>
76 <th>Price</th>
77 <th>Available</th>
78 <th>Category</th>
79 <th>Actions</th>
80 </tr>
81 </thead>
82 <tbody id="products-list">
83 <tr><td colspan="6" class="loading">Loading products...</td></tr>
84 </tbody>
85 </table>
86 </div>
87 </div>
88 </div>
89
90 <!-- Orders Tab -->
91 <div id="orders-tab" class="tab-content">
92 <div class="dashboard-section">
93 <h3>Store Orders</h3>
94 <div class="table-responsive">
95 <table id="orders-table">
96 <thead>
97 <tr>
98 <th>Order #</th>
99 <th>Client</th>
100 <th>Date</th>
101 <th>Status</th>
102 <th>Total</th>
103 <th>Actions</th>
104 </tr>
105 </thead>
106 <tbody id="orders-list">
107 <tr><td colspan="6" class="loading">Loading orders...</td></tr>
108 </tbody>
109 </table>
110 </div>
111 </div>
112 </div>
113
114 <!-- Employees Tab -->
115 <div id="employees-tab" class="tab-content">
116 <div class="dashboard-section">
117 <h3>Store Employees</h3>
118 <div class="table-responsive">
119 <table id="employees-table">
120 <thead>
121 <tr>
122 <th>ID</th>
123 <th>Name</th>
124 <th>Email</th>
125 <th>Date Hired</th>
126 <th>Permissions</th>
127 <th>Actions</th>
128 </tr>
129 </thead>
130 <tbody id="employees-list">
131 <tr><td colspan="6" class="loading">Loading employees...</td></tr>
132 </tbody>
133 </table>
134 </div>
135 </div>
136 </div>
137
138 <!-- Reports Tab -->
139 <div id="reports-tab" class="tab-content">
140 <div class="dashboard-section">
141 <h3>Generate Reports</h3>
142 <div class="form-group">
143 <label for="report-period">Report Period</label>
144 <select id="report-period">
145 <option value="daily">Daily</option>
146 <option value="weekly">Weekly</option>
147 <option value="monthly">Monthly</option>
148 <option value="quarterly">Quarterly</option>
149 <option value="yearly">Yearly</option>
150 <option value="custom">Custom Range</option>
151 </select>
152 </div>
153 <div id="custom-date-range" style="display: none;">
154 <div class="form-row">
155 <div class="form-group">
156 <label for="report-start">Start Date</label>
157 <input type="date" id="report-start">
158 </div>
159 <div class="form-group">
160 <label for="report-end">End Date</label>
161 <input type="date" id="report-end">
162 </div>
163 </div>
164 </div>
165 <div class="form-group">
166 <label for="report-type">Report Type</label>
167 <select id="report-type">
168 <option value="sales">Sales Report</option>
169 <option value="products">Products Report</option>
170 <option value="employees">Employees Report</option>
171 <option value="financial">Financial Report</option>
172 </select>
173 </div>
174 <button id="generate-store-report" class="btn-primary">Generate Report</button>
175 <div id="report-results" style="margin-top: 2rem;"></div>
176 </div>
177 </div>
178
179 <!-- Add Product Tab -->
180 <div id="add-product-tab" class="tab-content">
181 <div class="dashboard-section">
182 <h3>Add New Product</h3>
183 <form id="add-product-form" class="form">
184 <div class="form-group">
185 <label for="product-description">Description *</label>
186 <textarea id="product-description" rows="3" required></textarea>
187 </div>
188
189 <div class="form-row">
190 <div class="form-group">
191 <label for="product-price">Price ($) *</label>
192 <input type="number" id="product-price" step="0.01" min="0" required>
193 </div>
194 <div class="form-group">
195 <label for="product-availability">Availability *</label>
196 <input type="number" id="product-availability" min="0" required>
197 </div>
198 </div>
199
200 <div class="form-row">
201 <div class="form-group">
202 <label for="product-weight">Weight (kg) *</label>
203 <input type="number" id="product-weight" step="0.01" min="0" required>
204 </div>
205 <div class="form-group">
206 <label for="product-dimensions">Dimensions (WxLxD) *</label>
207 <input type="text" id="product-dimensions" placeholder="10x15x5" required>
208 </div>
209 </div>
210
211 <div class="form-row">
212 <div class="form-group">
213 <label for="product-production-time">Production Time (days) *</label>
214 <input type="number" id="product-production-time" min="1" required>
215 </div>
216 <div class="form-group">
217 <label for="product-category">Category *</label>
218 <select id="product-category" required>
219 <option value="">Select Category</option>
220 </select>
221 </div>
222 </div>
223
224 <div class="form-group">
225 <label for="product-images">Image URLs (one per line)</label>
226 <textarea id="product-images" rows="3" placeholder="https://example.com/image1.jpg"></textarea>
227 </div>
228
229 <div class="form-group">
230 <label for="product-colors">Colors (comma separated)</label>
231 <input type="text" id="product-colors" placeholder="Red, Blue, Green">
232 </div>
233
234 <button type="submit" class="btn-primary">Add Product</button>
235 </form>
236 </div>
237 </div>
238
239 <!-- Add Employee Tab -->
240 <div id="add-employee-tab" class="tab-content">
241 <div class="dashboard-section">
242 <h3>Add New Employee</h3>
243 <form id="add-employee-form" class="form">
244 <div class="form-row">
245 <div class="form-group">
246 <label for="employee-first-name">First Name *</label>
247 <input type="text" id="employee-first-name" required>
248 </div>
249 <div class="form-group">
250 <label for="employee-last-name">Last Name *</label>
251 <input type="text" id="employee-last-name" required>
252 </div>
253 </div>
254
255 <div class="form-group">
256 <label for="employee-ssn">SSN (13 digits) *</label>
257 <input type="text" id="employee-ssn" maxlength="13" pattern="\d{13}" required>
258 </div>
259
260 <div class="form-group">
261 <label for="employee-email">Email *</label>
262 <input type="email" id="employee-email" required>
263 </div>
264
265 <div class="form-group">
266 <label for="employee-password">Password *</label>
267 <input type="password" id="employee-password" required>
268 <small class="password-hint">At least 8 characters with uppercase, lowercase, number, and special character</small>
269 </div>
270
271 <div class="form-group">
272 <label for="employee-date-hire">Date of Hire *</label>
273 <input type="date" id="employee-date-hire" required>
274 </div>
275
276 <button type="submit" class="btn-primary">Add Employee</button>
277 </form>
278 </div>
279 </div>
280 </div>
281
282 <div id="no-store-message" style="display: none; text-align: center; padding: 3rem;">
283 <h3>You don't have any stores yet</h3>
284 <p>Please contact support if you believe this is an error.</p>
285 </div>
286</main>
287
288<div id="edit-product-modal" class="modal" style="display: none;">
289 <div class="modal-content modal-large">
290 <span class="close">&times;</span>
291 <h2>Edit Product</h2>
292 <form id="edit-product-form" class="form">
293 <input type="hidden" id="edit-product-code">
294
295 <div class="form-group">
296 <label for="edit-product-description">Description</label>
297 <textarea id="edit-product-description" rows="3"></textarea>
298 </div>
299
300 <div class="form-row">
301 <div class="form-group">
302 <label for="edit-product-price">Price ($)</label>
303 <input type="number" id="edit-product-price" step="0.01" min="0">
304 </div>
305 <div class="form-group">
306 <label for="edit-product-availability">Availability</label>
307 <input type="number" id="edit-product-availability" min="0">
308 </div>
309 </div>
310
311 <div class="form-row">
312 <div class="form-group">
313 <label for="edit-product-weight">Weight (kg)</label>
314 <input type="number" id="edit-product-weight" step="0.01" min="0">
315 </div>
316 <div class="form-group">
317 <label for="edit-product-dimensions">Dimensions</label>
318 <input type="text" id="edit-product-dimensions">
319 </div>
320 </div>
321
322 <div class="form-row">
323 <div class="form-group">
324 <label for="edit-product-production-time">Production Time (days)</label>
325 <input type="number" id="edit-product-production-time" min="1">
326 </div>
327 <div class="form-group">
328 <label for="edit-product-category">Category</label>
329 <select id="edit-product-category">
330 <option value="">Select Category</option>
331 </select>
332 </div>
333 </div>
334
335 <div class="form-group">
336 <label for="edit-product-images">Image URLs (one per line)</label>
337 <textarea id="edit-product-images" rows="3"></textarea>
338 </div>
339
340 <div class="form-group">
341 <label for="edit-product-colors">Colors (comma separated)</label>
342 <input type="text" id="edit-product-colors">
343 </div>
344
345 <button type="submit" class="btn-primary">Update Product</button>
346 </form>
347 </div>
348</div>
349
350<div id="edit-employee-modal" class="modal" style="display: none;">
351 <div class="modal-content">
352 <span class="close">&times;</span>
353 <h2>Edit Employee</h2>
354 <form id="edit-employee-form" class="form">
355 <input type="hidden" id="edit-employee-id">
356
357 <div class="form-row">
358 <div class="form-group">
359 <label for="edit-employee-first-name">First Name</label>
360 <input type="text" id="edit-employee-first-name">
361 </div>
362 <div class="form-group">
363 <label for="edit-employee-last-name">Last Name</label>
364 <input type="text" id="edit-employee-last-name">
365 </div>
366 </div>
367
368 <div class="form-group">
369 <label for="edit-employee-email">Email</label>
370 <input type="email" id="edit-employee-email">
371 </div>
372
373 <div class="form-group">
374 <label for="edit-employee-status">Status</label>
375 <select id="edit-employee-status">
376 <option value="active">Active</option>
377 <option value="suspended">Suspended</option>
378 <option value="promoted">Promote to Manager</option>
379 </select>
380 </div>
381
382 <button type="submit" class="btn-primary">Update Employee</button>
383 </form>
384 </div>
385</div>
386
387<footer>
388 <div class="footer-content">
389 <div class="footer-section">
390 <h3>Handcraft Marketplace</h3>
391 <p>Store Owner Portal</p>
392 </div>
393 <div class="footer-section">
394 <h3>Quick Links</h3>
395 <ul>
396 <li><a href="index.html">Home</a></li>
397 <li><a href="products.html">Products</a></li>
398 </ul>
399 </div>
400 </div>
401 <div class="footer-bottom">
402 <p>&copy; 2025 Handcraft Marketplace. All rights reserved.</p>
403 </div>
404</footer>
405
406<script src="script.js"></script>
407<script>
408 document.addEventListener('DOMContentLoaded', async () => {
409 const user = await loadUserData();
410 if (!user || user.userType !== 'store_owner') {
411 window.location.href = 'index.html';
412 return;
413 }
414
415 document.getElementById('owner-name').textContent = `${user.firstName} ${user.lastName || ''}`;
416
417 // Load stores
418 const stores = user.stores || [];
419 const storeSelect = document.getElementById('store-select');
420
421 if (stores.length === 0) {
422 storeSelect.innerHTML = '<option value="">No stores found</option>';
423 document.getElementById('no-store-message').style.display = 'block';
424 } else {
425 storeSelect.innerHTML = stores.map(store =>
426 `<option value="${store.store_id}">${store.name} (${store.store_id})</option>`
427 ).join('');
428
429 // Load first store by default
430 loadStoreData(stores[0].store_id);
431 }
432
433 storeSelect.addEventListener('change', (e) => {
434 if (e.target.value) {
435 loadStoreData(e.target.value);
436 }
437 });
438
439 // Tab switching
440 document.querySelectorAll('.tab-btn').forEach(btn => {
441 btn.addEventListener('click', () => {
442 document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
443 document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
444 btn.classList.add('active');
445 document.getElementById(`${btn.dataset.tab}-tab`).classList.add('active');
446 });
447 });
448
449 // Report period toggle
450 document.getElementById('report-period').addEventListener('change', (e) => {
451 document.getElementById('custom-date-range').style.display =
452 e.target.value === 'custom' ? 'block' : 'none';
453 });
454
455 // Load categories for product forms
456 loadCategories();
457
458 // Add product form
459 document.getElementById('add-product-form').addEventListener('submit', addProduct);
460
461 // Add employee form
462 document.getElementById('add-employee-form').addEventListener('submit', addEmployee);
463
464 // Generate report
465 document.getElementById('generate-store-report').addEventListener('click', generateStoreReport);
466 });
467
468 async function loadStoreData(storeId) {
469 document.getElementById('store-dashboard').style.display = 'block';
470 document.getElementById('no-store-message').style.display = 'none';
471
472 // Load store stats
473 try {
474 const response = await fetch(`/api/store-stats?storeId=${storeId}`);
475 const data = await response.json();
476
477 if (data.success) {
478 document.getElementById('total-products').textContent = data.stats.total_products || 0;
479 document.getElementById('total-orders').textContent = data.stats.total_orders || 0;
480 document.getElementById('total-revenue').textContent = `$${parseFloat(data.stats.total_revenue || 0).toFixed(2)}`;
481 document.getElementById('avg-rating').textContent = parseFloat(data.stats.avg_rating || 0).toFixed(1);
482 }
483 } catch (error) {
484 console.error('Error loading stats:', error);
485 }
486
487 // Load products
488 loadProducts(storeId);
489
490 // Load orders
491 loadStoreOrders(storeId);
492
493 // Load employees
494 loadEmployees(storeId);
495 }
496
497 async function loadProducts(storeId) {
498 try {
499 const response = await fetch(`/api/store-products?storeId=${storeId}`);
500 const data = await response.json();
501
502 const tbody = document.getElementById('products-list');
503 if (data.success && data.products.length > 0) {
504 tbody.innerHTML = data.products.map(product => `
505 <tr>
506 <td>${product.code}</td>
507 <td>${product.description.substring(0, 30)}${product.description.length > 30 ? '...' : ''}</td>
508 <td>$${parseFloat(product.price).toFixed(2)}</td>
509 <td>${product.availability}</td>
510 <td>${product.category_name || 'General'}</td>
511 <td>
512 <button class="btn-small" onclick="editProduct('${product.code}')">Edit</button>
513 <button class="btn-small" style="background: var(--danger);" onclick="deleteProduct('${product.code}', '${storeId}')">Delete</button>
514 </td>
515 </tr>
516 `).join('');
517 } else {
518 tbody.innerHTML = '<tr><td colspan="6">No products found</td></tr>';
519 }
520 } catch (error) {
521 console.error('Error loading products:', error);
522 document.getElementById('products-list').innerHTML = '<tr><td colspan="6">Error loading products</td></tr>';
523 }
524 }
525
526 async function loadStoreOrders(storeId) {
527 try {
528 const response = await fetch(`/api/store-orders?storeId=${storeId}`);
529 const data = await response.json();
530
531 const tbody = document.getElementById('orders-list');
532 if (data.success && data.orders.length > 0) {
533 tbody.innerHTML = data.orders.map(order => {
534 const total = order.items ? order.items.reduce((sum, item) =>
535 sum + (item.price * item.quantity), 0) : 0;
536 return `
537 <tr>
538 <td>${order.order_num}</td>
539 <td>${order.first_name} ${order.last_name}</td>
540 <td>${new Date(order.order_date).toLocaleDateString()}</td>
541 <td><span class="status-badge status-${order.status}">${order.status}</span></td>
542 <td>$${total.toFixed(2)}</td>
543 <td>
544 <button class="btn-small" onclick="viewOrder('${order.order_num}')">View</button>
545 </td>
546 </tr>
547 `;
548 }).join('');
549 } else {
550 tbody.innerHTML = '<tr><td colspan="6">No orders found</td></tr>';
551 }
552 } catch (error) {
553 console.error('Error loading orders:', error);
554 document.getElementById('orders-list').innerHTML = '<tr><td colspan="6">Error loading orders</td></tr>';
555 }
556 }
557
558 async function loadEmployees(storeId) {
559 try {
560 const response = await fetch(`/api/store-employees?storeId=${storeId}`);
561 const data = await response.json();
562
563 const tbody = document.getElementById('employees-list');
564 if (data.success && data.employees.length > 0) {
565 tbody.innerHTML = data.employees.map(emp => `
566 <tr>
567 <td>${emp.id}</td>
568 <td>${emp.first_name} ${emp.last_name}</td>
569 <td>${emp.email}</td>
570 <td>${emp.date_of_hire || 'N/A'}</td>
571 <td><span class="status-badge">${emp.permission_type || 'EMPLOYEE'}</span></td>
572 <td>
573 <button class="btn-small" onclick="editEmployee('${emp.id}')">Edit</button>
574 <button class="btn-small" style="background: var(--danger);" onclick="deleteEmployee('${emp.id}', '${storeId}')">Delete</button>
575 </td>
576 </tr>
577 `).join('');
578 } else {
579 tbody.innerHTML = '<tr><td colspan="6">No employees found</td></tr>';
580 }
581 } catch (error) {
582 console.error('Error loading employees:', error);
583 document.getElementById('employees-list').innerHTML = '<tr><td colspan="6">Error loading employees</td></tr>';
584 }
585 }
586
587 async function loadCategories() {
588 try {
589 const response = await fetch('/api/categories');
590 const data = await response.json();
591
592 if (data.success) {
593 const options = data.categories.map(cat =>
594 `<option value="${cat.category_id}">${cat.name}</option>`
595 ).join('');
596
597 document.getElementById('product-category').innerHTML = '<option value="">Select Category</option>' + options;
598 document.getElementById('edit-product-category').innerHTML = '<option value="">Select Category</option>' + options;
599 }
600 } catch (error) {
601 console.error('Error loading categories:', error);
602 }
603 }
604
605 async function addProduct(e) {
606 e.preventDefault();
607
608 const storeId = document.getElementById('store-select').value;
609 if (!storeId) {
610 alert('Please select a store first');
611 return;
612 }
613
614 // FIXED: Added validation for required fields
615 const description = document.getElementById('product-description').value;
616 const price = document.getElementById('product-price').value;
617 const availability = document.getElementById('product-availability').value;
618 const category = document.getElementById('product-category').value;
619
620 if (!description) {
621 alert('Please enter a product description');
622 return;
623 }
624
625 if (!price || parseFloat(price) <= 0) {
626 alert('Please enter a valid price');
627 return;
628 }
629
630 if (!availability || parseInt(availability) < 0) {
631 alert('Please enter a valid availability quantity');
632 return;
633 }
634
635 if (!category) {
636 alert('Please select a category');
637 return;
638 }
639
640 const formData = {
641 description: description,
642 price: parseFloat(price),
643 availability: parseInt(availability),
644 weight: parseFloat(document.getElementById('product-weight').value) || 0,
645 dimensions: document.getElementById('product-dimensions').value || '0x0x0',
646 production_time: parseInt(document.getElementById('product-production-time').value) || 1,
647 category_id: parseInt(category),
648 images: document.getElementById('product-images').value.split('\n').filter(url => url.trim()),
649 colors: document.getElementById('product-colors').value.split(',').map(c => c.trim()).filter(c => c),
650 storeId: storeId
651 };
652
653 try {
654 const response = await fetch('/api/add-product', {
655 method: 'POST',
656 headers: { 'Content-Type': 'application/json' },
657 body: JSON.stringify(formData)
658 });
659
660 const data = await response.json();
661
662 if (data.success) {
663 alert('Product added successfully! Product code: ' + data.productCode);
664 document.getElementById('add-product-form').reset();
665 loadProducts(storeId);
666 // Switch to products tab
667 document.querySelector('[data-tab="products"]').click();
668 } else {
669 alert(data.message || 'Failed to add product');
670 }
671 } catch (error) {
672 console.error('Error adding product:', error);
673 alert('An error occurred: ' + error.message);
674 }
675 }
676
677 async function addEmployee(e) {
678 e.preventDefault();
679
680 const storeId = document.getElementById('store-select').value;
681 if (!storeId) {
682 alert('Please select a store first');
683 return;
684 }
685
686 const password = document.getElementById('employee-password').value;
687 const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
688 if (!passwordRegex.test(password)) {
689 alert('Password must have at least 8 characters, including uppercase, lowercase, number and special character');
690 return;
691 }
692
693 const formData = {
694 firstName: document.getElementById('employee-first-name').value,
695 lastName: document.getElementById('employee-last-name').value,
696 ssn: document.getElementById('employee-ssn').value,
697 email: document.getElementById('employee-email').value,
698 password: password,
699 dateOfHire: document.getElementById('employee-date-hire').value,
700 storeId: storeId
701 };
702
703 try {
704 const response = await fetch('/api/register-employee', {
705 method: 'POST',
706 headers: { 'Content-Type': 'application/json' },
707 body: JSON.stringify(formData)
708 });
709
710 const data = await response.json();
711
712 if (data.success) {
713 alert('Employee added successfully!');
714 document.getElementById('add-employee-form').reset();
715 loadEmployees(storeId);
716 // Switch to employees tab
717 document.querySelector('[data-tab="employees"]').click();
718 } else {
719 alert(data.message || 'Failed to add employee');
720 }
721 } catch (error) {
722 console.error('Error adding employee:', error);
723 alert('An error occurred');
724 }
725 }
726
727 window.editProduct = async (productCode) => {
728 try {
729 const response = await fetch(`/api/product-by-code?code=${productCode}`);
730 const data = await response.json();
731
732 if (data.success) {
733 const product = data.product;
734
735 document.getElementById('edit-product-code').value = product.code;
736 document.getElementById('edit-product-description').value = product.description;
737 document.getElementById('edit-product-price').value = product.price;
738 document.getElementById('edit-product-availability').value = product.availability;
739 document.getElementById('edit-product-weight').value = product.weight;
740 document.getElementById('edit-product-dimensions').value = product.dimensions;
741 document.getElementById('edit-product-production-time').value = product.production_time;
742 document.getElementById('edit-product-category').value = product.category_id;
743
744 if (product.images) {
745 document.getElementById('edit-product-images').value = product.images.map(img => img.image).join('\n');
746 }
747
748 if (product.colors) {
749 document.getElementById('edit-product-colors').value = product.colors.map(c => c.color).join(', ');
750 }
751
752 document.getElementById('edit-product-modal').style.display = 'block';
753 }
754 } catch (error) {
755 console.error('Error loading product:', error);
756 alert('Failed to load product details');
757 }
758 };
759
760 document.getElementById('edit-product-form').addEventListener('submit', async (e) => {
761 e.preventDefault();
762
763 const storeId = document.getElementById('store-select').value;
764 const formData = {
765 code: document.getElementById('edit-product-code').value,
766 description: document.getElementById('edit-product-description').value,
767 price: parseFloat(document.getElementById('edit-product-price').value),
768 availability: parseInt(document.getElementById('edit-product-availability').value),
769 weight: parseFloat(document.getElementById('edit-product-weight').value),
770 dimensions: document.getElementById('edit-product-dimensions').value,
771 production_time: parseInt(document.getElementById('edit-product-production-time').value),
772 category_id: parseInt(document.getElementById('edit-product-category').value),
773 images: document.getElementById('edit-product-images').value.split('\n').filter(url => url.trim()),
774 colors: document.getElementById('edit-product-colors').value.split(',').map(c => c.trim()).filter(c => c),
775 storeId: storeId
776 };
777
778 try {
779 const response = await fetch('/api/update-product', {
780 method: 'POST',
781 headers: { 'Content-Type': 'application/json' },
782 body: JSON.stringify(formData)
783 });
784
785 const data = await response.json();
786
787 if (data.success) {
788 alert('Product updated successfully!');
789 document.getElementById('edit-product-modal').style.display = 'none';
790 loadProducts(storeId);
791 } else {
792 alert(data.message || 'Failed to update product');
793 }
794 } catch (error) {
795 console.error('Error updating product:', error);
796 alert('An error occurred');
797 }
798 });
799
800 window.deleteProduct = async (productCode, storeId) => {
801 if (!confirm('Are you sure you want to delete this product?')) {
802 return;
803 }
804
805 try {
806 const response = await fetch('/api/delete-product', {
807 method: 'POST',
808 headers: { 'Content-Type': 'application/json' },
809 body: JSON.stringify({ productCode, storeId })
810 });
811
812 const data = await response.json();
813
814 if (data.success) {
815 alert('Product deleted successfully!');
816 loadProducts(storeId);
817 } else {
818 alert(data.message || 'Failed to delete product');
819 }
820 } catch (error) {
821 console.error('Error deleting product:', error);
822 alert('An error occurred');
823 }
824 };
825
826 window.editEmployee = (employeeId) => {
827 // Load employee data and show edit modal
828 document.getElementById('edit-employee-id').value = employeeId;
829 document.getElementById('edit-employee-modal').style.display = 'block';
830 };
831
832 window.deleteEmployee = async (employeeId, storeId) => {
833 if (!confirm('Are you sure you want to delete this employee?')) {
834 return;
835 }
836
837 try {
838 const response = await fetch('/api/delete-employee', {
839 method: 'POST',
840 headers: { 'Content-Type': 'application/json' },
841 body: JSON.stringify({ employeeId, storeId })
842 });
843
844 const data = await response.json();
845
846 if (data.success) {
847 alert('Employee deleted successfully!');
848 loadEmployees(storeId);
849 } else {
850 alert(data.message || 'Failed to delete employee');
851 }
852 } catch (error) {
853 console.error('Error deleting employee:', error);
854 alert('An error occurred');
855 }
856 };
857
858 document.getElementById('edit-employee-form').addEventListener('submit', async (e) => {
859 e.preventDefault();
860
861 const storeId = document.getElementById('store-select').value;
862 const formData = {
863 employeeId: document.getElementById('edit-employee-id').value,
864 storeId: storeId,
865 firstName: document.getElementById('edit-employee-first-name').value,
866 lastName: document.getElementById('edit-employee-last-name').value,
867 email: document.getElementById('edit-employee-email').value,
868 status: document.getElementById('edit-employee-status').value
869 };
870
871 try {
872 const response = await fetch('/api/update-employee', {
873 method: 'POST',
874 headers: { 'Content-Type': 'application/json' },
875 body: JSON.stringify(formData)
876 });
877
878 const data = await response.json();
879
880 if (data.success) {
881 alert('Employee updated successfully!');
882 document.getElementById('edit-employee-modal').style.display = 'none';
883 loadEmployees(storeId);
884 } else {
885 alert(data.message || 'Failed to update employee');
886 }
887 } catch (error) {
888 console.error('Error updating employee:', error);
889 alert('An error occurred');
890 }
891 });
892
893 async function generateStoreReport() {
894 const storeId = document.getElementById('store-select').value;
895 const period = document.getElementById('report-period').value;
896 const type = document.getElementById('report-type').value;
897
898 let startDate, endDate;
899 if (period === 'custom') {
900 startDate = document.getElementById('report-start').value;
901 endDate = document.getElementById('report-end').value;
902 if (!startDate || !endDate) {
903 alert('Please select start and end dates');
904 return;
905 }
906 } else {
907 // Calculate dates based on period
908 const end = new Date();
909 const start = new Date();
910
911 switch(period) {
912 case 'daily':
913 start.setDate(start.getDate() - 1);
914 break;
915 case 'weekly':
916 start.setDate(start.getDate() - 7);
917 break;
918 case 'monthly':
919 start.setMonth(start.getMonth() - 1);
920 break;
921 case 'quarterly':
922 start.setMonth(start.getMonth() - 3);
923 break;
924 case 'yearly':
925 start.setFullYear(start.getFullYear() - 1);
926 break;
927 }
928
929 startDate = start.toISOString().split('T')[0];
930 endDate = end.toISOString().split('T')[0];
931 }
932
933 document.getElementById('report-results').innerHTML = `
934 <div class="dashboard-section">
935 <h4>Generating Report...</h4>
936 <div class="loading">Please wait</div>
937 </div>
938 `;
939
940 try {
941 const response = await fetch('/api/generate-report', {
942 method: 'POST',
943 headers: { 'Content-Type': 'application/json' },
944 body: JSON.stringify({
945 storeId,
946 period,
947 startDate,
948 endDate,
949 type
950 })
951 });
952
953 const data = await response.json();
954
955 if (data.success) {
956 document.getElementById('report-results').innerHTML = `
957 <div class="dashboard-section">
958 <h4>Report Generated Successfully</h4>
959 <p><strong>Report ID:</strong> ${data.reportId}</p>
960 <p><strong>Period:</strong> ${data.report.period}</p>
961 <p><strong>Date Range:</strong> ${data.report.startDate} to ${data.report.endDate}</p>
962 <p><strong>Type:</strong> ${data.report.type}</p>
963 <p><strong>Generated:</strong> ${new Date(data.report.generatedAt).toLocaleString()}</p>
964 <button class="btn-small" onclick="downloadReport('${data.reportId}')">Download Report</button>
965 </div>
966 `;
967 } else {
968 document.getElementById('report-results').innerHTML = `
969 <div class="dashboard-section">
970 <p class="error">Failed to generate report: ${data.message}</p>
971 </div>
972 `;
973 }
974 } catch (error) {
975 console.error('Error generating report:', error);
976 document.getElementById('report-results').innerHTML = `
977 <div class="dashboard-section">
978 <p class="error">An error occurred while generating the report</p>
979 </div>
980 `;
981 }
982 }
983
984 window.viewOrder = (orderNum) => {
985 alert(`View order ${orderNum} - Feature coming soon`);
986 };
987
988 window.downloadReport = (reportId) => {
989 alert(`Download report ${reportId} - Feature coming soon`);
990 };
991
992 // Close modal functionality
993 document.querySelectorAll('.modal .close').forEach(closeBtn => {
994 closeBtn.addEventListener('click', () => {
995 closeBtn.closest('.modal').style.display = 'none';
996 });
997 });
998
999 window.addEventListener('click', (event) => {
1000 if (event.target.classList.contains('modal')) {
1001 event.target.style.display = 'none';
1002 }
1003 });
1004</script>
1005</body>
1006</html>
Note: See TracBrowser for help on using the repository browser.