@20052507/dab-enterprise-bws 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,226 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import axios from 'axios';
3
+
4
+ const Reports = () => {
5
+ const [activeTab, setActiveTab] = useState('sales');
6
+ const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]);
7
+ const [salesReport, setSalesReport] = useState([]);
8
+ const [salesDetails, setSalesDetails] = useState([]);
9
+ const [stockReport, setStockReport] = useState([]);
10
+ const [loading, setLoading] = useState(false);
11
+
12
+ useEffect(() => {
13
+ if (activeTab === 'sales') {
14
+ fetchSalesReport();
15
+ } else {
16
+ fetchStockReport();
17
+ }
18
+ }, [activeTab, selectedDate]);
19
+
20
+ const fetchSalesReport = async () => {
21
+ setLoading(true);
22
+ try {
23
+ const [reportRes, detailsRes] = await Promise.all([
24
+ axios.get(`http://localhost:5000/api/sales/report/daily?date=${selectedDate}`),
25
+ axios.get(`http://localhost:5000/api/sales/report/daily/details?date=${selectedDate}`)
26
+ ]);
27
+ setSalesReport(reportRes.data);
28
+ setSalesDetails(detailsRes.data);
29
+ } catch (error) {
30
+ console.error('Error fetching sales report:', error);
31
+ } finally {
32
+ setLoading(false);
33
+ }
34
+ };
35
+
36
+ const fetchStockReport = async () => {
37
+ setLoading(true);
38
+ try {
39
+ const response = await axios.get('http://localhost:5000/api/stock/report/status');
40
+ setStockReport(response.data);
41
+ } catch (error) {
42
+ console.error('Error fetching stock report:', error);
43
+ } finally {
44
+ setLoading(false);
45
+ }
46
+ };
47
+
48
+ const handleDateChange = (e) => {
49
+ setSelectedDate(e.target.value);
50
+ };
51
+
52
+ const printReport = () => {
53
+ window.print();
54
+ };
55
+
56
+ return (
57
+ <div>
58
+ <h1 className="text-3xl font-bold text-gray-800 mb-8">Reports</h1>
59
+
60
+ <div className="bg-white rounded-lg shadow-md p-6">
61
+ <div className="flex space-x-4 mb-6">
62
+ <button
63
+ onClick={() => setActiveTab('sales')}
64
+ className={`px-6 py-2 rounded-lg transition ${
65
+ activeTab === 'sales' ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-700 hover:bg-gray-300'
66
+ }`}
67
+ >
68
+ Daily Sales Report
69
+ </button>
70
+ <button
71
+ onClick={() => setActiveTab('stock')}
72
+ className={`px-6 py-2 rounded-lg transition ${
73
+ activeTab === 'stock' ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-700 hover:bg-gray-300'
74
+ }`}
75
+ >
76
+ Stock Status Report
77
+ </button>
78
+ </div>
79
+
80
+ {activeTab === 'sales' && (
81
+ <div>
82
+ <div className="flex items-center space-x-4 mb-6">
83
+ <div>
84
+ <label className="block text-gray-700 font-medium mb-2">Select Date</label>
85
+ <input
86
+ type="date"
87
+ value={selectedDate}
88
+ onChange={handleDateChange}
89
+ className="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
90
+ />
91
+ </div>
92
+ <button
93
+ onClick={printReport}
94
+ className="mt-6 px-6 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition"
95
+ >
96
+ Print Report
97
+ </button>
98
+ </div>
99
+
100
+ {loading ? (
101
+ <div className="text-center">Loading...</div>
102
+ ) : (
103
+ <div>
104
+ <h2 className="text-xl font-bold text-gray-800 mb-4">Daily Sales Summary - {selectedDate}</h2>
105
+
106
+ {salesReport.length > 0 ? (
107
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
108
+ <div className="bg-blue-50 rounded-lg p-4">
109
+ <p className="text-gray-600">Total Sales</p>
110
+ <p className="text-2xl font-bold text-blue-600">{salesReport[0].TotalSales}</p>
111
+ </div>
112
+ <div className="bg-green-50 rounded-lg p-4">
113
+ <p className="text-gray-600">Total Quantity</p>
114
+ <p className="text-2xl font-bold text-green-600">{salesReport[0].TotalQuantity}</p>
115
+ </div>
116
+ <div className="bg-purple-50 rounded-lg p-4">
117
+ <p className="text-gray-600">Total Revenue</p>
118
+ <p className="text-2xl font-bold text-purple-600">RWF {parseFloat(salesReport[0].TotalRevenue).toFixed(2)}</p>
119
+ </div>
120
+ </div>
121
+ ) : (
122
+ <div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-6">
123
+ <p className="text-yellow-800">No sales data available for the selected date.</p>
124
+ </div>
125
+ )}
126
+
127
+ <h3 className="text-lg font-bold text-gray-800 mb-4">Sales Details</h3>
128
+ <div className="overflow-x-auto">
129
+ <table className="w-full">
130
+ <thead>
131
+ <tr className="bg-gray-100">
132
+ <th className="px-4 py-3 text-left">Product</th>
133
+ <th className="px-4 py-3 text-left">Category</th>
134
+ <th className="px-4 py-3 text-left">Qty Sold</th>
135
+ <th className="px-4 py-3 text-left">Unit Price</th>
136
+ <th className="px-4 py-3 text-left">Total</th>
137
+ <th className="px-4 py-3 text-left">Date</th>
138
+ </tr>
139
+ </thead>
140
+ <tbody>
141
+ {salesDetails.map((sale) => (
142
+ <tr key={sale.SalesID} className="border-b">
143
+ <td className="px-4 py-3">{sale.ProductName}</td>
144
+ <td className="px-4 py-3">{sale.Category}</td>
145
+ <td className="px-4 py-3">{sale.SoldQuantity}</td>
146
+ <td className="px-4 py-3">{parseFloat(sale.SoldUnitPrice).toFixed(2)}</td>
147
+ <td className="px-4 py-3">{parseFloat(sale.SoldTotalPrice).toFixed(2)}</td>
148
+ <td className="px-4 py-3">{sale.SalesDate}</td>
149
+ </tr>
150
+ ))}
151
+ </tbody>
152
+ </table>
153
+ </div>
154
+
155
+ {salesDetails.length === 0 && (
156
+ <div className="text-center py-8 text-gray-500">
157
+ No sales details available for the selected date.
158
+ </div>
159
+ )}
160
+ </div>
161
+ )}
162
+ </div>
163
+ )}
164
+
165
+ {activeTab === 'stock' && (
166
+ <div>
167
+ <div className="flex items-center space-x-4 mb-6">
168
+ <h2 className="text-xl font-bold text-gray-800">Daily Stock Status Report</h2>
169
+ <button
170
+ onClick={printReport}
171
+ className="px-6 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition"
172
+ >
173
+ Print Report
174
+ </button>
175
+ </div>
176
+
177
+ {loading ? (
178
+ <div className="text-center">Loading...</div>
179
+ ) : (
180
+ <div>
181
+ <div className="overflow-x-auto">
182
+ <table className="w-full">
183
+ <thead>
184
+ <tr className="bg-gray-100">
185
+ <th className="px-4 py-3 text-left">Product Name</th>
186
+ <th className="px-4 py-3 text-left">Category</th>
187
+ <th className="px-4 py-3 text-left">Stored Quantity</th>
188
+ <th className="px-4 py-3 text-left">Sold Quantity</th>
189
+ <th className="px-4 py-3 text-left">Remaining Quantity</th>
190
+ <th className="px-4 py-3 text-left">Unit Price</th>
191
+ <th className="px-4 py-3 text-left">Total Value</th>
192
+ <th className="px-4 py-3 text-left">Last Updated</th>
193
+ </tr>
194
+ </thead>
195
+ <tbody>
196
+ {stockReport.map((stock) => (
197
+ <tr key={stock.StockID} className="border-b">
198
+ <td className="px-4 py-3 font-medium">{stock.ProductName}</td>
199
+ <td className="px-4 py-3">{stock.Category}</td>
200
+ <td className="px-4 py-3">{stock.StoredQuantity}</td>
201
+ <td className="px-4 py-3">{stock.SoldQuantity}</td>
202
+ <td className="px-4 py-3 font-bold">{stock.RemainingQuantity}</td>
203
+ <td className="px-4 py-3">{parseFloat(stock.UnitPrice).toFixed(2)}</td>
204
+ <td className="px-4 py-3">{parseFloat(stock.TotalPrice).toFixed(2)}</td>
205
+ <td className="px-4 py-3">{new Date(stock.LastUpdated).toLocaleString()}</td>
206
+ </tr>
207
+ ))}
208
+ </tbody>
209
+ </table>
210
+ </div>
211
+
212
+ {stockReport.length === 0 && (
213
+ <div className="text-center py-8 text-gray-500">
214
+ No stock data available. Add products to generate stock report.
215
+ </div>
216
+ )}
217
+ </div>
218
+ )}
219
+ </div>
220
+ )}
221
+ </div>
222
+ </div>
223
+ );
224
+ };
225
+
226
+ export default Reports;
@@ -0,0 +1,239 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import axios from 'axios';
3
+
4
+ const Sales = () => {
5
+ const [sales, setSales] = useState([]);
6
+ const [products, setProducts] = useState([]);
7
+ const [formData, setFormData] = useState({
8
+ ProductID: '',
9
+ SoldQuantity: '',
10
+ SoldUnitPrice: '',
11
+ SalesDate: new Date().toISOString().split('T')[0]
12
+ });
13
+ const [editingSale, setEditingSale] = useState(null);
14
+ const [loading, setLoading] = useState(true);
15
+
16
+ useEffect(() => {
17
+ fetchData();
18
+ }, []);
19
+
20
+ const fetchData = async () => {
21
+ try {
22
+ const [salesRes, productsRes] = await Promise.all([
23
+ axios.get('http://localhost:5000/api/sales'),
24
+ axios.get('http://localhost:5000/api/products')
25
+ ]);
26
+ setSales(salesRes.data);
27
+ setProducts(productsRes.data);
28
+ } catch (error) {
29
+ console.error('Error fetching data:', error);
30
+ } finally {
31
+ setLoading(false);
32
+ }
33
+ };
34
+
35
+ const handleSubmit = async (e) => {
36
+ e.preventDefault();
37
+ try {
38
+ if (editingSale) {
39
+ await axios.put(`http://localhost:5000/api/sales/${editingSale.SalesID}`, formData);
40
+ } else {
41
+ await axios.post('http://localhost:5000/api/sales', formData);
42
+ }
43
+ setFormData({
44
+ ProductID: '',
45
+ SoldQuantity: '',
46
+ SoldUnitPrice: '',
47
+ SalesDate: new Date().toISOString().split('T')[0]
48
+ });
49
+ setEditingSale(null);
50
+ fetchData();
51
+ } catch (error) {
52
+ console.error('Error saving sale:', error);
53
+ alert(error.response?.data?.message || 'Error saving sale');
54
+ }
55
+ };
56
+
57
+ const handleEdit = (sale) => {
58
+ setEditingSale(sale);
59
+ setFormData({
60
+ ProductID: sale.ProductID,
61
+ SoldQuantity: sale.SoldQuantity,
62
+ SoldUnitPrice: sale.SoldUnitPrice,
63
+ SalesDate: sale.SalesDate
64
+ });
65
+ };
66
+
67
+ const handleDelete = async (id) => {
68
+ if (window.confirm('Are you sure you want to delete this sale?')) {
69
+ try {
70
+ await axios.delete(`http://localhost:5000/api/sales/${id}`);
71
+ fetchData();
72
+ } catch (error) {
73
+ console.error('Error deleting sale:', error);
74
+ alert('Error deleting sale');
75
+ }
76
+ }
77
+ };
78
+
79
+ const handleChange = (e) => {
80
+ setFormData({ ...formData, [e.target.name]: e.target.value });
81
+ };
82
+
83
+ const handleProductChange = (e) => {
84
+ const productId = e.target.value;
85
+ const product = products.find(p => p.ProductID === parseInt(productId));
86
+ setFormData({
87
+ ...formData,
88
+ ProductID: productId,
89
+ SoldUnitPrice: product ? product.UnitPrice : ''
90
+ });
91
+ };
92
+
93
+ if (loading) {
94
+ return <div className="text-center">Loading...</div>;
95
+ }
96
+
97
+ return (
98
+ <div>
99
+ <h1 className="text-3xl font-bold text-gray-800 mb-8">Sales Management</h1>
100
+
101
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
102
+ <div className="bg-white rounded-lg shadow-md p-6">
103
+ <h2 className="text-xl font-bold text-gray-800 mb-4">
104
+ {editingSale ? 'Edit Sale' : 'Record New Sale'}
105
+ </h2>
106
+ <form onSubmit={handleSubmit} className="space-y-4">
107
+ <div>
108
+ <label className="block text-gray-700 font-medium mb-2">Product</label>
109
+ <select
110
+ name="ProductID"
111
+ value={formData.ProductID}
112
+ onChange={handleProductChange}
113
+ className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
114
+ required
115
+ >
116
+ <option value="">Select a product</option>
117
+ {products.map((product) => (
118
+ <option key={product.ProductID} value={product.ProductID}>
119
+ {product.ProductName} - {product.Category} (Stock: {product.Quantity})
120
+ </option>
121
+ ))}
122
+ </select>
123
+ </div>
124
+
125
+ <div>
126
+ <label className="block text-gray-700 font-medium mb-2">Sold Quantity</label>
127
+ <input
128
+ type="number"
129
+ name="SoldQuantity"
130
+ value={formData.SoldQuantity}
131
+ onChange={handleChange}
132
+ className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
133
+ required
134
+ min="1"
135
+ />
136
+ </div>
137
+
138
+ <div>
139
+ <label className="block text-gray-700 font-medium mb-2">Sold Unit Price (RWF)</label>
140
+ <input
141
+ type="number"
142
+ name="SoldUnitPrice"
143
+ value={formData.SoldUnitPrice}
144
+ onChange={handleChange}
145
+ className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
146
+ required
147
+ min="0"
148
+ step="0.01"
149
+ />
150
+ </div>
151
+
152
+ <div>
153
+ <label className="block text-gray-700 font-medium mb-2">Sales Date</label>
154
+ <input
155
+ type="date"
156
+ name="SalesDate"
157
+ value={formData.SalesDate}
158
+ onChange={handleChange}
159
+ className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
160
+ required
161
+ />
162
+ </div>
163
+
164
+ <div className="flex space-x-4">
165
+ <button
166
+ type="submit"
167
+ className="flex-1 bg-green-600 text-white py-2 rounded-lg hover:bg-green-700 transition"
168
+ >
169
+ {editingSale ? 'Update Sale' : 'Record Sale'}
170
+ </button>
171
+ {editingSale && (
172
+ <button
173
+ type="button"
174
+ onClick={() => {
175
+ setEditingSale(null);
176
+ setFormData({
177
+ ProductID: '',
178
+ SoldQuantity: '',
179
+ SoldUnitPrice: '',
180
+ SalesDate: new Date().toISOString().split('T')[0]
181
+ });
182
+ }}
183
+ className="flex-1 bg-gray-400 text-white py-2 rounded-lg hover:bg-gray-500 transition"
184
+ >
185
+ Cancel
186
+ </button>
187
+ )}
188
+ </div>
189
+ </form>
190
+ </div>
191
+
192
+ <div className="bg-white rounded-lg shadow-md p-6">
193
+ <h2 className="text-xl font-bold text-gray-800 mb-4">Sales History</h2>
194
+ <div className="overflow-x-auto max-h-96 overflow-y-auto">
195
+ <table className="w-full">
196
+ <thead className="sticky top-0 bg-white">
197
+ <tr className="bg-gray-100">
198
+ <th className="px-4 py-3 text-left">Product</th>
199
+ <th className="px-4 py-3 text-left">Qty</th>
200
+ <th className="px-4 py-3 text-left">Price</th>
201
+ <th className="px-4 py-3 text-left">Total</th>
202
+ <th className="px-4 py-3 text-left">Date</th>
203
+ <th className="px-4 py-3 text-left">Actions</th>
204
+ </tr>
205
+ </thead>
206
+ <tbody>
207
+ {sales.map((sale) => (
208
+ <tr key={sale.SalesID} className="border-b">
209
+ <td className="px-4 py-3">{sale.ProductName}</td>
210
+ <td className="px-4 py-3">{sale.SoldQuantity}</td>
211
+ <td className="px-4 py-3">{parseFloat(sale.SoldUnitPrice).toFixed(2)}</td>
212
+ <td className="px-4 py-3">{parseFloat(sale.SoldTotalPrice).toFixed(2)}</td>
213
+ <td className="px-4 py-3">{sale.SalesDate}</td>
214
+ <td className="px-4 py-3">
215
+ <button
216
+ onClick={() => handleEdit(sale)}
217
+ className="text-blue-600 hover:text-blue-800 mr-2"
218
+ >
219
+ Edit
220
+ </button>
221
+ <button
222
+ onClick={() => handleDelete(sale.SalesID)}
223
+ className="text-red-600 hover:text-red-800"
224
+ >
225
+ Delete
226
+ </button>
227
+ </td>
228
+ </tr>
229
+ ))}
230
+ </tbody>
231
+ </table>
232
+ </div>
233
+ </div>
234
+ </div>
235
+ </div>
236
+ );
237
+ };
238
+
239
+ export default Sales;
@@ -0,0 +1,99 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import axios from 'axios';
3
+
4
+ const Stock = () => {
5
+ const [stockStatus, setStockStatus] = useState([]);
6
+ const [loading, setLoading] = useState(true);
7
+
8
+ useEffect(() => {
9
+ fetchStockStatus();
10
+ }, []);
11
+
12
+ const fetchStockStatus = async () => {
13
+ try {
14
+ const response = await axios.get('http://localhost:5000/api/stock');
15
+ setStockStatus(response.data);
16
+ } catch (error) {
17
+ console.error('Error fetching stock status:', error);
18
+ } finally {
19
+ setLoading(false);
20
+ }
21
+ };
22
+
23
+ if (loading) {
24
+ return <div className="text-center">Loading...</div>;
25
+ }
26
+
27
+ return (
28
+ <div>
29
+ <h1 className="text-3xl font-bold text-gray-800 mb-8">Stock Status</h1>
30
+
31
+ <div className="bg-white rounded-lg shadow-md p-6">
32
+ <h2 className="text-xl font-bold text-gray-800 mb-4">Current Stock Overview</h2>
33
+ <div className="overflow-x-auto">
34
+ <table className="w-full">
35
+ <thead>
36
+ <tr className="bg-gray-100">
37
+ <th className="px-4 py-3 text-left">Product Name</th>
38
+ <th className="px-4 py-3 text-left">Category</th>
39
+ <th className="px-4 py-3 text-left">Available Qty</th>
40
+ <th className="px-4 py-3 text-left">Sold Qty</th>
41
+ <th className="px-4 py-3 text-left">Remaining Qty</th>
42
+ <th className="px-4 py-3 text-left">Status</th>
43
+ </tr>
44
+ </thead>
45
+ <tbody>
46
+ {stockStatus.map((stock) => (
47
+ <tr key={stock.StockID} className="border-b">
48
+ <td className="px-4 py-3 font-medium">{stock.ProductName}</td>
49
+ <td className="px-4 py-3">{stock.Category}</td>
50
+ <td className="px-4 py-3">{stock.AvailableQuantity}</td>
51
+ <td className="px-4 py-3">{stock.SoldQuantity}</td>
52
+ <td className="px-4 py-3 font-bold">{stock.RemainingQuantity}</td>
53
+ <td className="px-4 py-3">
54
+ {stock.RemainingQuantity === 0 ? (
55
+ <span className="px-3 py-1 bg-red-100 text-red-800 rounded-full text-sm">Out of Stock</span>
56
+ ) : stock.RemainingQuantity < 10 ? (
57
+ <span className="px-3 py-1 bg-yellow-100 text-yellow-800 rounded-full text-sm">Low Stock</span>
58
+ ) : (
59
+ <span className="px-3 py-1 bg-green-100 text-green-800 rounded-full text-sm">In Stock</span>
60
+ )}
61
+ </td>
62
+ </tr>
63
+ ))}
64
+ </tbody>
65
+ </table>
66
+ </div>
67
+
68
+ {stockStatus.length === 0 && (
69
+ <div className="text-center py-8 text-gray-500">
70
+ No stock data available. Add products to see stock status.
71
+ </div>
72
+ )}
73
+ </div>
74
+
75
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-8">
76
+ <div className="bg-white rounded-lg shadow-md p-6">
77
+ <h3 className="text-lg font-bold text-gray-800 mb-2">Total Products</h3>
78
+ <p className="text-3xl font-bold text-blue-600">{stockStatus.length}</p>
79
+ </div>
80
+
81
+ <div className="bg-white rounded-lg shadow-md p-6">
82
+ <h3 className="text-lg font-bold text-gray-800 mb-2">Low Stock Items</h3>
83
+ <p className="text-3xl font-bold text-yellow-600">
84
+ {stockStatus.filter(s => s.RemainingQuantity < 10 && s.RemainingQuantity > 0).length}
85
+ </p>
86
+ </div>
87
+
88
+ <div className="bg-white rounded-lg shadow-md p-6">
89
+ <h3 className="text-lg font-bold text-gray-800 mb-2">Out of Stock</h3>
90
+ <p className="text-3xl font-bold text-red-600">
91
+ {stockStatus.filter(s => s.RemainingQuantity === 0).length}
92
+ </p>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ );
97
+ };
98
+
99
+ export default Stock;
@@ -0,0 +1,10 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ content: [
4
+ "./src/**/*.{js,jsx,ts,tsx}",
5
+ ],
6
+ theme: {
7
+ extend: {},
8
+ },
9
+ plugins: [],
10
+ }
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@20052507/dab-enterprise-bws",
3
+ "version": "1.0.1",
4
+ "description": "DAB Enterprise Ltd - Business Web Solution (BWS)",
5
+ "main": "backend-project/server.js",
6
+ "scripts": {
7
+ "start": "node backend-project/server.js",
8
+ "start-frontend": "cd frontend-project && npm start",
9
+ "install-all": "cd backend-project && npm install && cd ../frontend-project && npm install"
10
+ },
11
+ "keywords": [
12
+ "dab-enterprise",
13
+ "bws",
14
+ "sales-management"
15
+ ],
16
+ "license": "ISC"
17
+ }