@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,77 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
3
+ import Login from './pages/Login';
4
+ import Dashboard from './pages/Dashboard';
5
+ import Products from './pages/Products';
6
+ import Sales from './pages/Sales';
7
+ import Stock from './pages/Stock';
8
+ import Reports from './pages/Reports';
9
+ import Navbar from './components/Navbar';
10
+ import axios from 'axios';
11
+
12
+ axios.defaults.withCredentials = true;
13
+
14
+ function App() {
15
+ const [isAuthenticated, setIsAuthenticated] = useState(false);
16
+ const [loading, setLoading] = useState(true);
17
+
18
+ useEffect(() => {
19
+ checkAuth();
20
+ }, []);
21
+
22
+ const checkAuth = async () => {
23
+ try {
24
+ const response = await axios.get('http://localhost:5000/api/auth/check');
25
+ setIsAuthenticated(response.data.authenticated);
26
+ } catch (error) {
27
+ setIsAuthenticated(false);
28
+ } finally {
29
+ setLoading(false);
30
+ }
31
+ };
32
+
33
+ const handleLogin = () => {
34
+ setIsAuthenticated(true);
35
+ };
36
+
37
+ const handleLogout = async () => {
38
+ try {
39
+ await axios.post('http://localhost:5000/api/auth/logout');
40
+ setIsAuthenticated(false);
41
+ } catch (error) {
42
+ console.error('Logout error:', error);
43
+ }
44
+ };
45
+
46
+ if (loading) {
47
+ return (
48
+ <div className="min-h-screen flex items-center justify-center bg-gray-100">
49
+ <div className="text-xl">Loading...</div>
50
+ </div>
51
+ );
52
+ }
53
+
54
+ return (
55
+ <Router>
56
+ {!isAuthenticated ? (
57
+ <Login onLogin={handleLogin} />
58
+ ) : (
59
+ <div className="min-h-screen bg-gray-100">
60
+ <Navbar onLogout={handleLogout} />
61
+ <div className="container mx-auto px-4 py-8">
62
+ <Routes>
63
+ <Route path="/" element={<Navigate to="/dashboard" />} />
64
+ <Route path="/dashboard" element={<Dashboard />} />
65
+ <Route path="/products" element={<Products />} />
66
+ <Route path="/sales" element={<Sales />} />
67
+ <Route path="/stock" element={<Stock />} />
68
+ <Route path="/reports" element={<Reports />} />
69
+ </Routes>
70
+ </div>
71
+ </div>
72
+ )}
73
+ </Router>
74
+ );
75
+ }
76
+
77
+ export default App;
@@ -0,0 +1,47 @@
1
+ import React from 'react';
2
+ import { Link, useLocation } from 'react-router-dom';
3
+
4
+ const Navbar = ({ onLogout }) => {
5
+ const location = useLocation();
6
+
7
+ const isActive = (path) => {
8
+ return location.pathname === path ? 'bg-blue-700' : 'bg-blue-600 hover:bg-blue-700';
9
+ };
10
+
11
+ return (
12
+ <nav className="bg-blue-600 text-white shadow-lg">
13
+ <div className="container mx-auto px-4">
14
+ <div className="flex items-center justify-between h-16">
15
+ <div className="flex items-center space-x-2">
16
+ <span className="text-2xl font-bold">DAB Enterprise</span>
17
+ </div>
18
+ <div className="flex items-center space-x-1">
19
+ <Link to="/dashboard" className={`px-4 py-2 rounded transition ${isActive('/dashboard')}`}>
20
+ Dashboard
21
+ </Link>
22
+ <Link to="/products" className={`px-4 py-2 rounded transition ${isActive('/products')}`}>
23
+ Products
24
+ </Link>
25
+ <Link to="/sales" className={`px-4 py-2 rounded transition ${isActive('/sales')}`}>
26
+ Sales
27
+ </Link>
28
+ <Link to="/stock" className={`px-4 py-2 rounded transition ${isActive('/stock')}`}>
29
+ Stock Status
30
+ </Link>
31
+ <Link to="/reports" className={`px-4 py-2 rounded transition ${isActive('/reports')}`}>
32
+ Reports
33
+ </Link>
34
+ <button
35
+ onClick={onLogout}
36
+ className="ml-4 px-4 py-2 bg-red-500 hover:bg-red-600 rounded transition"
37
+ >
38
+ Logout
39
+ </button>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </nav>
44
+ );
45
+ };
46
+
47
+ export default Navbar;
@@ -0,0 +1,12 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ body {
6
+ margin: 0;
7
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
8
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
9
+ sans-serif;
10
+ -webkit-font-smoothing: antialiased;
11
+ -moz-osx-font-smoothing: grayscale;
12
+ }
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import App from './App';
4
+ import './index.css';
5
+
6
+ const root = ReactDOM.createRoot(document.getElementById('root'));
7
+ root.render(
8
+ <React.StrictMode>
9
+ <App />
10
+ </React.StrictMode>
11
+ );
@@ -0,0 +1,122 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import axios from 'axios';
3
+
4
+ const Dashboard = () => {
5
+ const [stats, setStats] = useState({
6
+ totalProducts: 0,
7
+ totalSales: 0,
8
+ totalRevenue: 0,
9
+ lowStock: 0
10
+ });
11
+ const [loading, setLoading] = useState(true);
12
+
13
+ useEffect(() => {
14
+ fetchStats();
15
+ }, []);
16
+
17
+ const fetchStats = async () => {
18
+ try {
19
+ const [productsRes, salesRes, stockRes] = await Promise.all([
20
+ axios.get('http://localhost:5000/api/products'),
21
+ axios.get('http://localhost:5000/api/sales'),
22
+ axios.get('http://localhost:5000/api/stock')
23
+ ]);
24
+
25
+ const products = productsRes.data;
26
+ const sales = salesRes.data;
27
+ const stock = stockRes.data;
28
+
29
+ const totalRevenue = sales.reduce((sum, sale) => sum + parseFloat(sale.SoldTotalPrice), 0);
30
+ const lowStock = stock.filter(item => item.RemainingQuantity < 10).length;
31
+
32
+ setStats({
33
+ totalProducts: products.length,
34
+ totalSales: sales.length,
35
+ totalRevenue: totalRevenue.toFixed(2),
36
+ lowStock
37
+ });
38
+ } catch (error) {
39
+ console.error('Error fetching stats:', error);
40
+ } finally {
41
+ setLoading(false);
42
+ }
43
+ };
44
+
45
+ if (loading) {
46
+ return <div className="text-center">Loading...</div>;
47
+ }
48
+
49
+ return (
50
+ <div>
51
+ <h1 className="text-3xl font-bold text-gray-800 mb-8">Dashboard</h1>
52
+
53
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
54
+ <div className="bg-white rounded-lg shadow-md p-6">
55
+ <div className="flex items-center justify-between">
56
+ <div>
57
+ <p className="text-gray-600 text-sm">Total Products</p>
58
+ <p className="text-3xl font-bold text-blue-600">{stats.totalProducts}</p>
59
+ </div>
60
+ <div className="bg-blue-100 p-3 rounded-full">
61
+ <svg className="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
62
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
63
+ </svg>
64
+ </div>
65
+ </div>
66
+ </div>
67
+
68
+ <div className="bg-white rounded-lg shadow-md p-6">
69
+ <div className="flex items-center justify-between">
70
+ <div>
71
+ <p className="text-gray-600 text-sm">Total Sales</p>
72
+ <p className="text-3xl font-bold text-green-600">{stats.totalSales}</p>
73
+ </div>
74
+ <div className="bg-green-100 p-3 rounded-full">
75
+ <svg className="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
76
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
77
+ </svg>
78
+ </div>
79
+ </div>
80
+ </div>
81
+
82
+ <div className="bg-white rounded-lg shadow-md p-6">
83
+ <div className="flex items-center justify-between">
84
+ <div>
85
+ <p className="text-gray-600 text-sm">Total Revenue</p>
86
+ <p className="text-3xl font-bold text-purple-600">RWF {stats.totalRevenue}</p>
87
+ </div>
88
+ <div className="bg-purple-100 p-3 rounded-full">
89
+ <svg className="w-8 h-8 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
90
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
91
+ </svg>
92
+ </div>
93
+ </div>
94
+ </div>
95
+
96
+ <div className="bg-white rounded-lg shadow-md p-6">
97
+ <div className="flex items-center justify-between">
98
+ <div>
99
+ <p className="text-gray-600 text-sm">Low Stock Items</p>
100
+ <p className="text-3xl font-bold text-red-600">{stats.lowStock}</p>
101
+ </div>
102
+ <div className="bg-red-100 p-3 rounded-full">
103
+ <svg className="w-8 h-8 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
104
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
105
+ </svg>
106
+ </div>
107
+ </div>
108
+ </div>
109
+ </div>
110
+
111
+ <div className="bg-white rounded-lg shadow-md p-6">
112
+ <h2 className="text-xl font-bold text-gray-800 mb-4">Welcome to DAB Enterprise Ltd</h2>
113
+ <p className="text-gray-600">
114
+ This Business Web Solution helps manage your products, sales, and inventory efficiently.
115
+ Use the navigation menu to access different features of the system.
116
+ </p>
117
+ </div>
118
+ </div>
119
+ );
120
+ };
121
+
122
+ export default Dashboard;
@@ -0,0 +1,88 @@
1
+ import React, { useState } from 'react';
2
+ import axios from 'axios';
3
+
4
+ const Login = ({ onLogin }) => {
5
+ const [username, setUsername] = useState('');
6
+ const [password, setPassword] = useState('');
7
+ const [error, setError] = useState('');
8
+ const [loading, setLoading] = useState(false);
9
+
10
+ const handleSubmit = async (e) => {
11
+ e.preventDefault();
12
+ setError('');
13
+ setLoading(true);
14
+
15
+ try {
16
+ const response = await axios.post('http://localhost:5000/api/auth/login', {
17
+ username,
18
+ password
19
+ });
20
+
21
+ if (response.data.message === 'Login successful') {
22
+ onLogin();
23
+ }
24
+ } catch (error) {
25
+ setError(error.response?.data?.message || 'Login failed. Please try again.');
26
+ } finally {
27
+ setLoading(false);
28
+ }
29
+ };
30
+
31
+ return (
32
+ <div className="min-h-screen bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center px-4">
33
+ <div className="bg-white rounded-lg shadow-xl p-8 w-full max-w-md">
34
+ <div className="text-center mb-8">
35
+ <h1 className="text-3xl font-bold text-gray-800 mb-2">DAB Enterprise Ltd</h1>
36
+ <p className="text-gray-600">Business Web Solution</p>
37
+ </div>
38
+
39
+ <form onSubmit={handleSubmit} className="space-y-6">
40
+ <div>
41
+ <label className="block text-gray-700 font-medium mb-2">Username</label>
42
+ <input
43
+ type="text"
44
+ value={username}
45
+ onChange={(e) => setUsername(e.target.value)}
46
+ className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
47
+ placeholder="Enter your username"
48
+ required
49
+ />
50
+ </div>
51
+
52
+ <div>
53
+ <label className="block text-gray-700 font-medium mb-2">Password</label>
54
+ <input
55
+ type="password"
56
+ value={password}
57
+ onChange={(e) => setPassword(e.target.value)}
58
+ className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
59
+ placeholder="Enter your password"
60
+ required
61
+ />
62
+ </div>
63
+
64
+ {error && (
65
+ <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
66
+ {error}
67
+ </div>
68
+ )}
69
+
70
+ <button
71
+ type="submit"
72
+ disabled={loading}
73
+ className="w-full bg-blue-600 text-white py-3 rounded-lg font-medium hover:bg-blue-700 transition disabled:bg-gray-400"
74
+ >
75
+ {loading ? 'Logging in...' : 'Login'}
76
+ </button>
77
+ </form>
78
+
79
+ <div className="mt-6 text-center text-sm text-gray-600">
80
+ <p>Default credentials:</p>
81
+ <p className="font-medium">Username: admin | Password: Admin@123</p>
82
+ </div>
83
+ </div>
84
+ </div>
85
+ );
86
+ };
87
+
88
+ export default Login;
@@ -0,0 +1,208 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import axios from 'axios';
3
+
4
+ const Products = () => {
5
+ const [products, setProducts] = useState([]);
6
+ const [formData, setFormData] = useState({
7
+ ProductName: '',
8
+ Category: '',
9
+ Quantity: '',
10
+ UnitPrice: ''
11
+ });
12
+ const [editingProduct, setEditingProduct] = useState(null);
13
+ const [loading, setLoading] = useState(true);
14
+
15
+ useEffect(() => {
16
+ fetchProducts();
17
+ }, []);
18
+
19
+ const fetchProducts = async () => {
20
+ try {
21
+ const response = await axios.get('http://localhost:5000/api/products');
22
+ setProducts(response.data);
23
+ } catch (error) {
24
+ console.error('Error fetching products:', error);
25
+ } finally {
26
+ setLoading(false);
27
+ }
28
+ };
29
+
30
+ const handleSubmit = async (e) => {
31
+ e.preventDefault();
32
+ try {
33
+ if (editingProduct) {
34
+ await axios.put(`http://localhost:5000/api/products/${editingProduct.ProductID}`, formData);
35
+ } else {
36
+ await axios.post('http://localhost:5000/api/products', formData);
37
+ }
38
+ setFormData({ ProductName: '', Category: '', Quantity: '', UnitPrice: '' });
39
+ setEditingProduct(null);
40
+ fetchProducts();
41
+ } catch (error) {
42
+ console.error('Error saving product:', error);
43
+ alert('Error saving product');
44
+ }
45
+ };
46
+
47
+ const handleEdit = (product) => {
48
+ setEditingProduct(product);
49
+ setFormData({
50
+ ProductName: product.ProductName,
51
+ Category: product.Category,
52
+ Quantity: product.Quantity,
53
+ UnitPrice: product.UnitPrice
54
+ });
55
+ };
56
+
57
+ const handleDelete = async (id) => {
58
+ if (window.confirm('Are you sure you want to delete this product?')) {
59
+ try {
60
+ await axios.delete(`http://localhost:5000/api/products/${id}`);
61
+ fetchProducts();
62
+ } catch (error) {
63
+ console.error('Error deleting product:', error);
64
+ alert('Error deleting product');
65
+ }
66
+ }
67
+ };
68
+
69
+ const handleChange = (e) => {
70
+ setFormData({ ...formData, [e.target.name]: e.target.value });
71
+ };
72
+
73
+ if (loading) {
74
+ return <div className="text-center">Loading...</div>;
75
+ }
76
+
77
+ return (
78
+ <div>
79
+ <h1 className="text-3xl font-bold text-gray-800 mb-8">Products Management</h1>
80
+
81
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
82
+ <div className="bg-white rounded-lg shadow-md p-6">
83
+ <h2 className="text-xl font-bold text-gray-800 mb-4">
84
+ {editingProduct ? 'Edit Product' : 'Add New Product'}
85
+ </h2>
86
+ <form onSubmit={handleSubmit} className="space-y-4">
87
+ <div>
88
+ <label className="block text-gray-700 font-medium mb-2">Product Name</label>
89
+ <input
90
+ type="text"
91
+ name="ProductName"
92
+ value={formData.ProductName}
93
+ onChange={handleChange}
94
+ className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
95
+ required
96
+ />
97
+ </div>
98
+
99
+ <div>
100
+ <label className="block text-gray-700 font-medium mb-2">Category</label>
101
+ <input
102
+ type="text"
103
+ name="Category"
104
+ value={formData.Category}
105
+ onChange={handleChange}
106
+ className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
107
+ required
108
+ />
109
+ </div>
110
+
111
+ <div>
112
+ <label className="block text-gray-700 font-medium mb-2">Quantity</label>
113
+ <input
114
+ type="number"
115
+ name="Quantity"
116
+ value={formData.Quantity}
117
+ onChange={handleChange}
118
+ className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
119
+ required
120
+ min="0"
121
+ />
122
+ </div>
123
+
124
+ <div>
125
+ <label className="block text-gray-700 font-medium mb-2">Unit Price (RWF)</label>
126
+ <input
127
+ type="number"
128
+ name="UnitPrice"
129
+ value={formData.UnitPrice}
130
+ onChange={handleChange}
131
+ className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
132
+ required
133
+ min="0"
134
+ step="0.01"
135
+ />
136
+ </div>
137
+
138
+ <div className="flex space-x-4">
139
+ <button
140
+ type="submit"
141
+ className="flex-1 bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition"
142
+ >
143
+ {editingProduct ? 'Update Product' : 'Add Product'}
144
+ </button>
145
+ {editingProduct && (
146
+ <button
147
+ type="button"
148
+ onClick={() => {
149
+ setEditingProduct(null);
150
+ setFormData({ ProductName: '', Category: '', Quantity: '', UnitPrice: '' });
151
+ }}
152
+ className="flex-1 bg-gray-400 text-white py-2 rounded-lg hover:bg-gray-500 transition"
153
+ >
154
+ Cancel
155
+ </button>
156
+ )}
157
+ </div>
158
+ </form>
159
+ </div>
160
+
161
+ <div className="bg-white rounded-lg shadow-md p-6">
162
+ <h2 className="text-xl font-bold text-gray-800 mb-4">Product List</h2>
163
+ <div className="overflow-x-auto">
164
+ <table className="w-full">
165
+ <thead>
166
+ <tr className="bg-gray-100">
167
+ <th className="px-4 py-3 text-left">Name</th>
168
+ <th className="px-4 py-3 text-left">Category</th>
169
+ <th className="px-4 py-3 text-left">Qty</th>
170
+ <th className="px-4 py-3 text-left">Price</th>
171
+ <th className="px-4 py-3 text-left">Total</th>
172
+ <th className="px-4 py-3 text-left">Actions</th>
173
+ </tr>
174
+ </thead>
175
+ <tbody>
176
+ {products.map((product) => (
177
+ <tr key={product.ProductID} className="border-b">
178
+ <td className="px-4 py-3">{product.ProductName}</td>
179
+ <td className="px-4 py-3">{product.Category}</td>
180
+ <td className="px-4 py-3">{product.Quantity}</td>
181
+ <td className="px-4 py-3">{parseFloat(product.UnitPrice).toFixed(2)}</td>
182
+ <td className="px-4 py-3">{parseFloat(product.TotalPrice).toFixed(2)}</td>
183
+ <td className="px-4 py-3">
184
+ <button
185
+ onClick={() => handleEdit(product)}
186
+ className="text-blue-600 hover:text-blue-800 mr-2"
187
+ >
188
+ Edit
189
+ </button>
190
+ <button
191
+ onClick={() => handleDelete(product.ProductID)}
192
+ className="text-red-600 hover:text-red-800"
193
+ >
194
+ Delete
195
+ </button>
196
+ </td>
197
+ </tr>
198
+ ))}
199
+ </tbody>
200
+ </table>
201
+ </div>
202
+ </div>
203
+ </div>
204
+ </div>
205
+ );
206
+ };
207
+
208
+ export default Products;