source: interfejs/store-owner.html@ 591278c

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

Fixed Product and Category adding

  • Property mode set to 100644
File size: 46.0 KB
Line 
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
154 <div id="custom-date-range" style="display: none;">
155 <div class="form-row">
156 <div class="form-group">
157 <label for="report-start">Start Date</label>
158 <input type="date" id="report-start">
159 </div>
160 <div class="form-group">
161 <label for="report-end">End Date</label>
162 <input type="date" id="report-end">
163 </div>
164 </div>
165 </div>
166
167 <div class="form-group">
168 <label for="report-type">Report Type</label>
169 <select id="report-type">
170 <option value="sales">Sales Report</option>
171 <option value="products">Products Report</option>
172 <option value="employees">Employees Report</option>
173 <option value="financial">Financial Report</option>
174 </select>
175 </div>
176
177 <button id="generate-store-report" class="btn-primary">Generate Report</button>
178
179 <div id="report-results" style="margin-top: 2rem;"></div>
180 </div>
181 </div>
182
183 <!-- Add Product Tab -->
184 <div id="add-product-tab" class="tab-content">
185 <div class="dashboard-section">
186 <h3>Add New Product</h3>
187 <form id="add-product-form" class="form">
188 <div class="form-group">
189 <label for="product-description">Description *</label>
190 <textarea id="product-description" rows="3" required></textarea>
191 </div>
192
193 <div class="form-row">
194 <div class="form-group">
195 <label for="product-price">Price ($) *</label>
196 <input type="number" id="product-price" step="0.01" min="0" required>
197 </div>
198 <div class="form-group">
199 <label for="product-availability">Availability *</label>
200 <input type="number" id="product-availability" min="0" required>
201 </div>
202 </div>
203
204 <div class="form-row">
205 <div class="form-group">
206 <label for="product-weight">Weight (kg) *</label>
207 <input type="number" id="product-weight" step="0.01" min="0" required>
208 </div>
209 <div class="form-group">
210 <label for="product-dimensions">Dimensions (WxLxD) *</label>
211 <input type="text" id="product-dimensions" placeholder="10x15x5" required>
212 </div>
213 </div>
214
215 <div class="form-row">
216 <div class="form-group">
217 <label for="product-production-time">Production Time (days) *</label>
218 <input type="number" id="product-production-time" min="1" required>
219 </div>
220 <div class="form-group category-group">
221 <label for="product-category">Category *</label>
222 <div class="category-input-group">
223 <select id="product-category" required>
224 <option value="">Select Category</option>
225 </select>
226 <button type="button" id="add-category-btn" class="btn-small btn-secondary">➕ New Category</button>
227 </div>
228 </div>
229 </div>
230
231 <div class="form-group">
232 <label for="product-images">Image URLs (one per line)</label>
233 <textarea id="product-images" rows="3" placeholder="https://example.com/image1.jpg"></textarea>
234 </div>
235
236 <div class="form-group">
237 <label for="product-colors">Colors (comma separated)</label>
238 <input type="text" id="product-colors" placeholder="Red, Blue, Green">
239 </div>
240
241 <button type="submit" class="btn-primary">Add Product</button>
242 </form>
243 </div>
244 </div>
245
246 <!-- Add Employee Tab -->
247 <div id="add-employee-tab" class="tab-content">
248 <div class="dashboard-section">
249 <h3>Add New Employee</h3>
250 <form id="add-employee-form" class="form">
251 <div class="form-row">
252 <div class="form-group">
253 <label for="employee-first-name">First Name *</label>
254 <input type="text" id="employee-first-name" required>
255 </div>
256 <div class="form-group">
257 <label for="employee-last-name">Last Name *</label>
258 <input type="text" id="employee-last-name" required>
259 </div>
260 </div>
261
262 <div class="form-group">
263 <label for="employee-ssn">SSN (13 digits) *</label>
264 <input type="text" id="employee-ssn" maxlength="13" pattern="\d{13}" required>
265 </div>
266
267 <div class="form-group">
268 <label for="employee-email">Email *</label>
269 <input type="email" id="employee-email" required>
270 </div>
271
272 <div class="form-group">
273 <label for="employee-password">Password *</label>
274 <input type="password" id="employee-password" required>
275 <small class="password-hint">At least 8 characters with uppercase, lowercase, number, and special character</small>
276 </div>
277
278 <div class="form-group">
279 <label for="employee-date-hire">Date of Hire *</label>
280 <input type="date" id="employee-date-hire" required>
281 </div>
282
283 <button type="submit" class="btn-primary">Add Employee</button>
284 </form>
285 </div>
286 </div>
287 </div>
288
289 <div id="no-store-message" style="display: none; text-align: center; padding: 3rem;">
290 <h3>You don't have any stores yet</h3>
291 <p>Please contact support if you believe this is an error.</p>
292 </div>
293</main>
294
295<!-- Edit Product Modal -->
296<div id="edit-product-modal" class="modal" style="display: none;">
297 <div class="modal-content modal-large">
298 <span class="close">&times;</span>
299 <h2>Edit Product</h2>
300 <form id="edit-product-form" class="form">
301 <input type="hidden" id="edit-product-code">
302
303 <div class="form-group">
304 <label for="edit-product-description">Description</label>
305 <textarea id="edit-product-description" rows="3"></textarea>
306 </div>
307
308 <div class="form-row">
309 <div class="form-group">
310 <label for="edit-product-price">Price ($)</label>
311 <input type="number" id="edit-product-price" step="0.01" min="0">
312 </div>
313 <div class="form-group">
314 <label for="edit-product-availability">Availability</label>
315 <input type="number" id="edit-product-availability" min="0">
316 </div>
317 </div>
318
319 <div class="form-row">
320 <div class="form-group">
321 <label for="edit-product-weight">Weight (kg)</label>
322 <input type="number" id="edit-product-weight" step="0.01" min="0">
323 </div>
324 <div class="form-group">
325 <label for="edit-product-dimensions">Dimensions</label>
326 <input type="text" id="edit-product-dimensions">
327 </div>
328 </div>
329
330 <div class="form-row">
331 <div class="form-group">
332 <label for="edit-product-production-time">Production Time (days)</label>
333 <input type="number" id="edit-product-production-time" min="1">
334 </div>
335 <div class="form-group category-group">
336 <label for="edit-product-category">Category</label>
337 <div class="category-input-group">
338 <select id="edit-product-category">
339 <option value="">Select Category</option>
340 </select>
341 <button type="button" id="edit-add-category-btn" class="btn-small btn-secondary">➕ New Category</button>
342 </div>
343 </div>
344 </div>
345
346 <div class="form-group">
347 <label for="edit-product-images">Image URLs (one per line)</label>
348 <textarea id="edit-product-images" rows="3"></textarea>
349 </div>
350
351 <div class="form-group">
352 <label for="edit-product-colors">Colors (comma separated)</label>
353 <input type="text" id="edit-product-colors">
354 </div>
355
356 <button type="submit" class="btn-primary">Update Product</button>
357 </form>
358 </div>
359</div>
360
361<!-- Edit Employee Modal -->
362<div id="edit-employee-modal" class="modal" style="display: none;">
363 <div class="modal-content">
364 <span class="close">&times;</span>
365 <h2>Edit Employee</h2>
366 <form id="edit-employee-form" class="form">
367 <input type="hidden" id="edit-employee-id">
368
369 <div class="form-row">
370 <div class="form-group">
371 <label for="edit-employee-first-name">First Name</label>
372 <input type="text" id="edit-employee-first-name">
373 </div>
374 <div class="form-group">
375 <label for="edit-employee-last-name">Last Name</label>
376 <input type="text" id="edit-employee-last-name">
377 </div>
378 </div>
379
380 <div class="form-group">
381 <label for="edit-employee-email">Email</label>
382 <input type="email" id="edit-employee-email">
383 </div>
384
385 <div class="form-group">
386 <label for="edit-employee-status">Status</label>
387 <select id="edit-employee-status">
388 <option value="active">Active</option>
389 <option value="suspended">Suspended</option>
390 <option value="promoted">Promote to Manager</option>
391 </select>
392 </div>
393
394 <button type="submit" class="btn-primary">Update Employee</button>
395 </form>
396 </div>
397</div>
398
399<!-- Add Category Modal -->
400<div id="add-category-modal" class="modal" style="display: none;">
401 <div class="modal-content">
402 <span class="close">&times;</span>
403 <h2>Create New Category</h2>
404 <form id="add-category-form" class="form">
405 <div class="form-group">
406 <label for="category-name">Category Name *</label>
407 <input type="text" id="category-name" required>
408 </div>
409
410 <div class="form-group">
411 <label for="category-description">Description</label>
412 <textarea id="category-description" rows="3" placeholder="Optional description"></textarea>
413 </div>
414
415 <div class="form-group">
416 <label for="category-parent">Parent Category (optional)</label>
417 <select id="category-parent">
418 <option value="">None (Top Level Category)</option>
419 </select>
420 </div>
421
422 <button type="submit" class="btn-primary">Create Category</button>
423 <button type="button" id="cancel-category-btn" class="btn-secondary">Cancel</button>
424 </form>
425 </div>
426</div>
427
428<footer>
429 <div class="footer-content">
430 <div class="footer-section">
431 <h3>Handcraft Marketplace</h3>
432 <p>Store Owner Portal</p>
433 </div>
434 <div class="footer-section">
435 <h3>Quick Links</h3>
436 <ul>
437 <li><a href="index.html">Home</a></li>
438 <li><a href="products.html">Products</a></li>
439 </ul>
440 </div>
441 </div>
442 <div class="footer-bottom">
443 <p>&copy; 2025 Handcraft Marketplace. All rights reserved.</p>
444 </div>
445</footer>
446
447<script src="script.js"></script>
448<script>
449 document.addEventListener('DOMContentLoaded', async () => {
450 const user = await loadUserData();
451
452 if (!user || user.userType !== 'store_owner') {
453 window.location.href = 'index.html';
454 return;
455 }
456
457 document.getElementById('owner-name').textContent = `${user.firstName} ${user.lastName || ''}`;
458
459 // Load stores
460 const stores = user.stores || [];
461 const storeSelect = document.getElementById('store-select');
462
463 if (stores.length === 0) {
464 storeSelect.innerHTML = '<option value="">No stores found</option>';
465 document.getElementById('no-store-message').style.display = 'block';
466 } else {
467 storeSelect.innerHTML = stores.map(store =>
468 `<option value="${store.store_id}">${store.name} (${store.store_id})</option>`
469 ).join('');
470
471 // Load first store by default
472 loadStoreData(stores[0].store_id);
473 }
474
475 storeSelect.addEventListener('change', (e) => {
476 if (e.target.value) {
477 loadStoreData(e.target.value);
478 }
479 });
480
481 // Tab switching
482 document.querySelectorAll('.tab-btn').forEach(btn => {
483 btn.addEventListener('click', () => {
484 document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
485 document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
486 btn.classList.add('active');
487 document.getElementById(`${btn.dataset.tab}-tab`).classList.add('active');
488 });
489 });
490
491 // Report period toggle
492 document.getElementById('report-period').addEventListener('change', (e) => {
493 document.getElementById('custom-date-range').style.display =
494 e.target.value === 'custom' ? 'block' : 'none';
495 });
496
497 // Load categories for product forms
498 loadCategories();
499
500 // Add product form
501 document.getElementById('add-product-form').addEventListener('submit', addProduct);
502
503 // Add employee form
504 document.getElementById('add-employee-form').addEventListener('submit', addEmployee);
505
506 // Generate report
507 document.getElementById('generate-store-report').addEventListener('click', generateStoreReport);
508
509 // Add Category button functionality
510 document.getElementById('add-category-btn').addEventListener('click', () => {
511 openCategoryModal();
512 });
513
514 document.getElementById('edit-add-category-btn').addEventListener('click', () => {
515 openCategoryModal('edit');
516 });
517
518 // Category form submission
519 document.getElementById('add-category-form').addEventListener('submit', createCategory);
520
521 // Cancel button
522 document.getElementById('cancel-category-btn').addEventListener('click', () => {
523 document.getElementById('add-category-modal').style.display = 'none';
524 });
525 });
526
527 async function loadStoreData(storeId) {
528 document.getElementById('store-dashboard').style.display = 'block';
529 document.getElementById('no-store-message').style.display = 'none';
530
531 // Load store stats
532 try {
533 const response = await fetch(`/api/store-stats?storeId=${storeId}`);
534 const data = await response.json();
535
536 if (data.success) {
537 document.getElementById('total-products').textContent = data.stats.total_products || 0;
538 document.getElementById('total-orders').textContent = data.stats.total_orders || 0;
539 document.getElementById('total-revenue').textContent = `$${parseFloat(data.stats.total_revenue || 0).toFixed(2)}`;
540 document.getElementById('avg-rating').textContent = parseFloat(data.stats.avg_rating || 0).toFixed(1);
541 }
542 } catch (error) {
543 console.error('Error loading stats:', error);
544 }
545
546 // Load products
547 loadProducts(storeId);
548
549 // Load orders
550 loadStoreOrders(storeId);
551
552 // Load employees
553 loadEmployees(storeId);
554 }
555
556 async function loadProducts(storeId) {
557 try {
558 const response = await fetch(`/api/store-products?storeId=${storeId}`);
559 const data = await response.json();
560 const tbody = document.getElementById('products-list');
561
562 if (data.success && data.products.length > 0) {
563 tbody.innerHTML = data.products.map(product => `
564 <tr>
565 <td>${product.code}</td>
566 <td>${product.description.substring(0, 30)}${product.description.length > 30 ? '...' : ''}</td>
567 <td>$${parseFloat(product.price).toFixed(2)}</td>
568 <td>${product.availability}</td>
569 <td>${product.category_name || 'General'}</td>
570 <td>
571 <button class="btn-small" onclick="editProduct('${product.code}')">Edit</button>
572 <button class="btn-small" style="background: var(--danger);" onclick="deleteProduct('${product.code}', '${storeId}')">Delete</button>
573 </td>
574 </tr>
575 `).join('');
576 } else {
577 tbody.innerHTML = '<tr><td colspan="6">No products found</td></tr>';
578 }
579 } catch (error) {
580 console.error('Error loading products:', error);
581 document.getElementById('products-list').innerHTML = '<tr><td colspan="6">Error loading products</td></tr>';
582 }
583 }
584
585 async function loadStoreOrders(storeId) {
586 try {
587 const response = await fetch(`/api/store-orders?storeId=${storeId}`);
588 const data = await response.json();
589 const tbody = document.getElementById('orders-list');
590
591 if (data.success && data.orders.length > 0) {
592 tbody.innerHTML = data.orders.map(order => {
593 const total = order.items ? order.items.reduce((sum, item) =>
594 sum + (item.price * item.quantity), 0) : 0;
595 return `
596 <tr>
597 <td>${order.order_num}</td>
598 <td>${order.first_name} ${order.last_name}</td>
599 <td>${new Date(order.order_date).toLocaleDateString()}</td>
600 <td><span class="status-badge status-${order.status}">${order.status}</span></td>
601 <td>$${total.toFixed(2)}</td>
602 <td>
603 <button class="btn-small" onclick="viewOrder('${order.order_num}')">View</button>
604 </td>
605 </tr>
606 `;
607 }).join('');
608 } else {
609 tbody.innerHTML = '<tr><td colspan="6">No orders found</td></tr>';
610 }
611 } catch (error) {
612 console.error('Error loading orders:', error);
613 document.getElementById('orders-list').innerHTML = '<tr><td colspan="6">Error loading orders</td></tr>';
614 }
615 }
616
617 async function loadEmployees(storeId) {
618 try {
619 const response = await fetch(`/api/store-employees?storeId=${storeId}`);
620 const data = await response.json();
621 const tbody = document.getElementById('employees-list');
622
623 if (data.success && data.employees.length > 0) {
624 tbody.innerHTML = data.employees.map(emp => `
625 <tr>
626 <td>${emp.id}</td>
627 <td>${emp.first_name} ${emp.last_name}</td>
628 <td>${emp.email}</td>
629 <td>${emp.date_of_hire || 'N/A'}</td>
630 <td><span class="status-badge">${emp.permission_type || 'EMPLOYEE'}</span></td>
631 <td>
632 <button class="btn-small" onclick="editEmployee('${emp.id}')">Edit</button>
633 <button class="btn-small" style="background: var(--danger);" onclick="deleteEmployee('${emp.id}', '${storeId}')">Delete</button>
634 </td>
635 </tr>
636 `).join('');
637 } else {
638 tbody.innerHTML = '<tr><td colspan="6">No employees found</td></tr>';
639 }
640 } catch (error) {
641 console.error('Error loading employees:', error);
642 document.getElementById('employees-list').innerHTML = '<tr><td colspan="6">Error loading employees</td></tr>';
643 }
644 }
645
646 async function loadCategories() {
647 try {
648 const response = await fetch('/api/categories');
649 const data = await response.json();
650
651 if (data.success) {
652 const options = data.categories.map(cat =>
653 `<option value="${cat.category_id}">${cat.name}${cat.parent_name ? ` (subcategory of ${cat.parent_name})` : ''}</option>`
654 ).join('');
655
656 document.getElementById('product-category').innerHTML = '<option value="">Select Category</option>' + options;
657 document.getElementById('edit-product-category').innerHTML = '<option value="">Select Category</option>' + options;
658
659 // Also load for category parent dropdown
660 const parentOptions = data.categories.map(cat =>
661 `<option value="${cat.category_id}">${cat.name}</option>`
662 ).join('');
663 document.getElementById('category-parent').innerHTML = '<option value="">None (Top Level Category)</option>' + parentOptions;
664 }
665 } catch (error) {
666 console.error('Error loading categories:', error);
667 }
668 }
669
670 function openCategoryModal(source = 'add') {
671 // Store which form triggered this
672 document.getElementById('add-category-modal').dataset.source = source;
673 document.getElementById('add-category-modal').style.display = 'block';
674 document.getElementById('add-category-form').reset();
675 }
676
677 async function createCategory(e) {
678 e.preventDefault();
679
680 const categoryName = document.getElementById('category-name').value.trim();
681 const categoryDescription = document.getElementById('category-description').value.trim();
682 const parentId = document.getElementById('category-parent').value;
683
684 if (!categoryName) {
685 alert('Please enter a category name');
686 return;
687 }
688
689 const categoryData = {
690 name: categoryName,
691 description: categoryDescription,
692 parentId: parentId || null
693 };
694
695 try {
696 const response = await fetch('/api/create-category', {
697 method: 'POST',
698 headers: { 'Content-Type': 'application/json' },
699 body: JSON.stringify(categoryData)
700 });
701
702 const data = await response.json();
703
704 if (data.success) {
705 alert(`Category "${categoryName}" created successfully!`);
706 document.getElementById('add-category-modal').style.display = 'none';
707
708 // Reload categories in all dropdowns
709 await loadCategories();
710
711 // Auto-select the new category if it came from add product form
712 const source = document.getElementById('add-category-modal').dataset.source;
713 if (source === 'edit') {
714 document.getElementById('edit-product-category').value = data.category.id;
715 } else {
716 document.getElementById('product-category').value = data.category.id;
717 }
718 } else {
719 alert(data.message || 'Failed to create category');
720 }
721 } catch (error) {
722 console.error('Error creating category:', error);
723 alert('An error occurred while creating the category');
724 }
725 }
726
727 async function addProduct(e) {
728 e.preventDefault();
729
730 const storeId = document.getElementById('store-select').value;
731
732 if (!storeId) {
733 alert('Please select a store first');
734 return;
735 }
736
737 // FIXED: Added validation for required fields
738 const description = document.getElementById('product-description').value;
739 const price = document.getElementById('product-price').value;
740 const availability = document.getElementById('product-availability').value;
741 const category = document.getElementById('product-category').value;
742
743 if (!description) {
744 alert('Please enter a product description');
745 return;
746 }
747
748 if (!price || parseFloat(price) <= 0) {
749 alert('Please enter a valid price');
750 return;
751 }
752
753 if (!availability || parseInt(availability) < 0) {
754 alert('Please enter a valid availability quantity');
755 return;
756 }
757
758 if (!category) {
759 alert('Please select a category');
760 return;
761 }
762
763 const formData = {
764 description: description,
765 price: parseFloat(price),
766 availability: parseInt(availability),
767 weight: parseFloat(document.getElementById('product-weight').value) || 0,
768 dimensions: document.getElementById('product-dimensions').value || '0x0x0',
769 production_time: parseInt(document.getElementById('product-production-time').value) || 1,
770 category_id: parseInt(category),
771 images: document.getElementById('product-images').value.split('\n').filter(url => url.trim()),
772 colors: document.getElementById('product-colors').value.split(',').map(c => c.trim()).filter(c => c),
773 storeId: storeId
774 };
775
776 try {
777 const response = await fetch('/api/add-product', {
778 method: 'POST',
779 headers: { 'Content-Type': 'application/json' },
780 body: JSON.stringify(formData)
781 });
782
783 const data = await response.json();
784
785 if (data.success) {
786 alert('Product added successfully! Product code: ' + data.productCode);
787 document.getElementById('add-product-form').reset();
788 loadProducts(storeId);
789 // Switch to products tab
790 document.querySelector('[data-tab="products"]').click();
791 } else {
792 alert(data.message || 'Failed to add product');
793 }
794 } catch (error) {
795 console.error('Error adding product:', error);
796 alert('An error occurred: ' + error.message);
797 }
798 }
799
800 async function addEmployee(e) {
801 e.preventDefault();
802
803 const storeId = document.getElementById('store-select').value;
804
805 if (!storeId) {
806 alert('Please select a store first');
807 return;
808 }
809
810 const password = document.getElementById('employee-password').value;
811 const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
812
813 if (!passwordRegex.test(password)) {
814 alert('Password must have at least 8 characters, including uppercase, lowercase, number and special character');
815 return;
816 }
817
818 const formData = {
819 firstName: document.getElementById('employee-first-name').value,
820 lastName: document.getElementById('employee-last-name').value,
821 ssn: document.getElementById('employee-ssn').value,
822 email: document.getElementById('employee-email').value,
823 password: password,
824 dateOfHire: document.getElementById('employee-date-hire').value,
825 storeId: storeId
826 };
827
828 try {
829 const response = await fetch('/api/register-employee', {
830 method: 'POST',
831 headers: { 'Content-Type': 'application/json' },
832 body: JSON.stringify(formData)
833 });
834
835 const data = await response.json();
836
837 if (data.success) {
838 alert('Employee added successfully!');
839 document.getElementById('add-employee-form').reset();
840 loadEmployees(storeId);
841 // Switch to employees tab
842 document.querySelector('[data-tab="employees"]').click();
843 } else {
844 alert(data.message || 'Failed to add employee');
845 }
846 } catch (error) {
847 console.error('Error adding employee:', error);
848 alert('An error occurred');
849 }
850 }
851
852 window.editProduct = async (productCode) => {
853 try {
854 const response = await fetch(`/api/product-by-code?code=${productCode}`);
855 const data = await response.json();
856
857 if (data.success) {
858 const product = data.product;
859
860 document.getElementById('edit-product-code').value = product.code;
861 document.getElementById('edit-product-description').value = product.description;
862 document.getElementById('edit-product-price').value = product.price;
863 document.getElementById('edit-product-availability').value = product.availability;
864 document.getElementById('edit-product-weight').value = product.weight;
865 document.getElementById('edit-product-dimensions').value = product.dimensions;
866 document.getElementById('edit-product-production-time').value = product.production_time;
867 document.getElementById('edit-product-category').value = product.category_id;
868
869 if (product.images) {
870 document.getElementById('edit-product-images').value = product.images.map(img => img.image).join('\n');
871 }
872
873 if (product.colors) {
874 document.getElementById('edit-product-colors').value = product.colors.map(c => c.color).join(', ');
875 }
876
877 document.getElementById('edit-product-modal').style.display = 'block';
878 }
879 } catch (error) {
880 console.error('Error loading product:', error);
881 alert('Failed to load product details');
882 }
883 };
884
885 document.getElementById('edit-product-form').addEventListener('submit', async (e) => {
886 e.preventDefault();
887
888 const storeId = document.getElementById('store-select').value;
889
890 const formData = {
891 code: document.getElementById('edit-product-code').value,
892 description: document.getElementById('edit-product-description').value,
893 price: parseFloat(document.getElementById('edit-product-price').value),
894 availability: parseInt(document.getElementById('edit-product-availability').value),
895 weight: parseFloat(document.getElementById('edit-product-weight').value),
896 dimensions: document.getElementById('edit-product-dimensions').value,
897 production_time: parseInt(document.getElementById('edit-product-production-time').value),
898 category_id: parseInt(document.getElementById('edit-product-category').value),
899 images: document.getElementById('edit-product-images').value.split('\n').filter(url => url.trim()),
900 colors: document.getElementById('edit-product-colors').value.split(',').map(c => c.trim()).filter(c => c),
901 storeId: storeId
902 };
903
904 try {
905 const response = await fetch('/api/update-product', {
906 method: 'POST',
907 headers: { 'Content-Type': 'application/json' },
908 body: JSON.stringify(formData)
909 });
910
911 const data = await response.json();
912
913 if (data.success) {
914 alert('Product updated successfully!');
915 document.getElementById('edit-product-modal').style.display = 'none';
916 loadProducts(storeId);
917 } else {
918 alert(data.message || 'Failed to update product');
919 }
920 } catch (error) {
921 console.error('Error updating product:', error);
922 alert('An error occurred');
923 }
924 });
925
926 window.deleteProduct = async (productCode, storeId) => {
927 if (!confirm('Are you sure you want to delete this product?')) {
928 return;
929 }
930
931 try {
932 const response = await fetch('/api/delete-product', {
933 method: 'POST',
934 headers: { 'Content-Type': 'application/json' },
935 body: JSON.stringify({ productCode, storeId })
936 });
937
938 const data = await response.json();
939
940 if (data.success) {
941 alert('Product deleted successfully!');
942 loadProducts(storeId);
943 } else {
944 alert(data.message || 'Failed to delete product');
945 }
946 } catch (error) {
947 console.error('Error deleting product:', error);
948 alert('An error occurred');
949 }
950 };
951
952 window.editEmployee = (employeeId) => {
953 // Load employee data and show edit modal
954 document.getElementById('edit-employee-id').value = employeeId;
955 document.getElementById('edit-employee-modal').style.display = 'block';
956 };
957
958 window.deleteEmployee = async (employeeId, storeId) => {
959 if (!confirm('Are you sure you want to delete this employee?')) {
960 return;
961 }
962
963 try {
964 const response = await fetch('/api/delete-employee', {
965 method: 'POST',
966 headers: { 'Content-Type': 'application/json' },
967 body: JSON.stringify({ employeeId, storeId })
968 });
969
970 const data = await response.json();
971
972 if (data.success) {
973 alert('Employee deleted successfully!');
974 loadEmployees(storeId);
975 } else {
976 alert(data.message || 'Failed to delete employee');
977 }
978 } catch (error) {
979 console.error('Error deleting employee:', error);
980 alert('An error occurred');
981 }
982 };
983
984 document.getElementById('edit-employee-form').addEventListener('submit', async (e) => {
985 e.preventDefault();
986
987 const storeId = document.getElementById('store-select').value;
988
989 const formData = {
990 employeeId: document.getElementById('edit-employee-id').value,
991 storeId: storeId,
992 firstName: document.getElementById('edit-employee-first-name').value,
993 lastName: document.getElementById('edit-employee-last-name').value,
994 email: document.getElementById('edit-employee-email').value,
995 status: document.getElementById('edit-employee-status').value
996 };
997
998 try {
999 const response = await fetch('/api/update-employee', {
1000 method: 'POST',
1001 headers: { 'Content-Type': 'application/json' },
1002 body: JSON.stringify(formData)
1003 });
1004
1005 const data = await response.json();
1006
1007 if (data.success) {
1008 alert('Employee updated successfully!');
1009 document.getElementById('edit-employee-modal').style.display = 'none';
1010 loadEmployees(storeId);
1011 } else {
1012 alert(data.message || 'Failed to update employee');
1013 }
1014 } catch (error) {
1015 console.error('Error updating employee:', error);
1016 alert('An error occurred');
1017 }
1018 });
1019
1020 async function generateStoreReport() {
1021 const storeId = document.getElementById('store-select').value;
1022 const period = document.getElementById('report-period').value;
1023 const type = document.getElementById('report-type').value;
1024
1025 let startDate, endDate;
1026
1027 if (period === 'custom') {
1028 startDate = document.getElementById('report-start').value;
1029 endDate = document.getElementById('report-end').value;
1030
1031 if (!startDate || !endDate) {
1032 alert('Please select start and end dates');
1033 return;
1034 }
1035 } else {
1036 // Calculate dates based on period
1037 const end = new Date();
1038 const start = new Date();
1039
1040 switch(period) {
1041 case 'daily':
1042 start.setDate(start.getDate() - 1);
1043 break;
1044 case 'weekly':
1045 start.setDate(start.getDate() - 7);
1046 break;
1047 case 'monthly':
1048 start.setMonth(start.getMonth() - 1);
1049 break;
1050 case 'quarterly':
1051 start.setMonth(start.getMonth() - 3);
1052 break;
1053 case 'yearly':
1054 start.setFullYear(start.getFullYear() - 1);
1055 break;
1056 }
1057
1058 startDate = start.toISOString().split('T')[0];
1059 endDate = end.toISOString().split('T')[0];
1060 }
1061
1062 document.getElementById('report-results').innerHTML = `
1063 <div class="dashboard-section">
1064 <h4>Generating Report...</h4>
1065 <div class="loading">Please wait</div>
1066 </div>
1067 `;
1068
1069 try {
1070 const response = await fetch('/api/generate-report', {
1071 method: 'POST',
1072 headers: { 'Content-Type': 'application/json' },
1073 body: JSON.stringify({
1074 storeId,
1075 period,
1076 startDate,
1077 endDate,
1078 type
1079 })
1080 });
1081
1082 const data = await response.json();
1083
1084 if (data.success) {
1085 document.getElementById('report-results').innerHTML = `
1086 <div class="dashboard-section">
1087 <h4>Report Generated Successfully</h4>
1088 <p><strong>Report ID:</strong> ${data.reportId}</p>
1089 <p><strong>Period:</strong> ${data.report.period}</p>
1090 <p><strong>Date Range:</strong> ${data.report.startDate} to ${data.report.endDate}</p>
1091 <p><strong>Type:</strong> ${data.report.type}</p>
1092 <p><strong>Generated:</strong> ${new Date(data.report.generatedAt).toLocaleString()}</p>
1093 <button class="btn-small" onclick="downloadReport('${data.reportId}')">Download Report</button>
1094 </div>
1095 `;
1096 } else {
1097 document.getElementById('report-results').innerHTML = `
1098 <div class="dashboard-section">
1099 <p class="error">Failed to generate report: ${data.message}</p>
1100 </div>
1101 `;
1102 }
1103 } catch (error) {
1104 console.error('Error generating report:', error);
1105 document.getElementById('report-results').innerHTML = `
1106 <div class="dashboard-section">
1107 <p class="error">An error occurred while generating the report</p>
1108 </div>
1109 `;
1110 }
1111 }
1112
1113 window.viewOrder = (orderNum) => {
1114 alert(`View order ${orderNum} - Feature coming soon`);
1115 };
1116
1117 window.downloadReport = (reportId) => {
1118 alert(`Download report ${reportId} - Feature coming soon`);
1119 };
1120
1121 // Close modal functionality
1122 document.querySelectorAll('.modal .close').forEach(closeBtn => {
1123 closeBtn.addEventListener('click', () => {
1124 closeBtn.closest('.modal').style.display = 'none';
1125 });
1126 });
1127
1128 window.addEventListener('click', (event) => {
1129 if (event.target.classList.contains('modal')) {
1130 event.target.style.display = 'none';
1131 }
1132 });
1133</script>
1134</body>
1135</html>
Note: See TracBrowser for help on using the repository browser.