source: interfejs/store-owner.html

main
Last change on this file was 79fff4f, checked in by Klimentina Efremova <klimentina08642@…>, 7 months ago

Improved employee logging in and changing password

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