Compare commits
2 Commits
fe76deb2d9
...
03bf26a8a1
Author | SHA1 | Date | |
---|---|---|---|
|
03bf26a8a1 | ||
|
d656e183b6 |
@ -3,7 +3,11 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom';
|
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom';
|
||||||
import PanelAdministratora from './components/PanelAdministratora';
|
import PanelAdministratora from './components/PanelAdministratora';
|
||||||
import ZarzadzanieProduktami from './components/ZarzadzanieProduktami';
|
import ZarzadzanieProduktami from './components/ZarzadzanieProduktami';
|
||||||
import Transakcje from './components/Transakcje';
|
import DodawanieProduktu from './components/DodawanieProduktów';
|
||||||
|
import EdycjaProduktu from './components/EdycjaProduktu';
|
||||||
|
import ZarzadzanieTransakcjami from './components/ZarzadzanieTransakcjami';
|
||||||
|
import DodawanieTransakcji from './components/DodawanieTransakcji';
|
||||||
|
import EdycjaTransakcji from './components/EdycjaTransakcji';
|
||||||
import NavBar from './components/NavBar';
|
import NavBar from './components/NavBar';
|
||||||
import Sidebar from './components/Sidebar';
|
import Sidebar from './components/Sidebar';
|
||||||
import Wydatki from './components/Wydatki';
|
import Wydatki from './components/Wydatki';
|
||||||
@ -43,22 +47,18 @@ const App = () => {
|
|||||||
)}
|
)}
|
||||||
<div className="w-3/4">
|
<div className="w-3/4">
|
||||||
<Routes>
|
<Routes>
|
||||||
{/* Przekierowanie na stronę logowania, jeśli token jest pusty */}
|
|
||||||
<Route path="/" element={token ? <Navigate to="/transakcje" /> : <Navigate to="/login" />} />
|
<Route path="/" element={token ? <Navigate to="/transakcje" /> : <Navigate to="/login" />} />
|
||||||
|
|
||||||
{/* Strona logowania */}
|
|
||||||
<Route path="/login" element={token ? <Navigate to="/transakcje" /> : <Login setToken={setToken} />} />
|
<Route path="/login" element={token ? <Navigate to="/transakcje" /> : <Login setToken={setToken} />} />
|
||||||
|
<Route path="/transakcje" element={token ? <ZarzadzanieTransakcjami /> : <Navigate to="/login" />} />
|
||||||
{/* Chronione ścieżki */}
|
<Route path="/transakcje/dodaj" element={token ? <DodawanieTransakcji /> : <Navigate to="/login" />} />
|
||||||
<Route path="/transakcje" element={token ? <Transakcje /> : <Navigate to="/login" />} />
|
<Route path="/transakcje/edytuj/:id" element={token ? <EdycjaTransakcji /> : <Navigate to="/login" />} />
|
||||||
<Route path="/panel" element={token ? <PanelAdministratora /> : <Navigate to="/login" />} />
|
<Route path="/panel" element={token ? <PanelAdministratora /> : <Navigate to="/login" />} />
|
||||||
<Route path="/produkty" element={token ? <ZarzadzanieProduktami /> : <Navigate to="/login" />} />
|
<Route path="/produkty" element={token ? <ZarzadzanieProduktami /> : <Navigate to="/login" />} />
|
||||||
|
<Route path="/produkty/dodaj" element={token ? <DodawanieProduktu /> : <Navigate to="/login" />} />
|
||||||
|
<Route path="/produkty/edytuj/:id" element={token ? <EdycjaProduktu /> : <Navigate to="/login" />} />
|
||||||
<Route path="/wydatki" element={token ? <Wydatki /> : <Navigate to="/login" />} />
|
<Route path="/wydatki" element={token ? <Wydatki /> : <Navigate to="/login" />} />
|
||||||
<Route path="/raporty" element={token ? <Raporty /> : <Navigate to="/login" />} />
|
<Route path="/raporty" element={token ? <Raporty /> : <Navigate to="/login" />} />
|
||||||
<Route path="/harmonogram" element={token ? <Harmonogram /> : <Navigate to="/login" />} />
|
<Route path="/harmonogram" element={token ? <Harmonogram /> : <Navigate to="/login" />} />
|
||||||
|
|
||||||
{/* Przekierowanie dla nieznanych ścieżek */}
|
|
||||||
<Route path="*" element={<Navigate to="/login" />} />
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
130
firm/src/components/DodawanieProduktów.js
Normal file
130
firm/src/components/DodawanieProduktów.js
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
const DodawanieProduktu = () => {
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const [newProduct, setNewProduct] = useState({
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
price: '',
|
||||||
|
type: '1',
|
||||||
|
availability: '',
|
||||||
|
});
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleInputChange = (event) => {
|
||||||
|
const { name, value } = event.target;
|
||||||
|
setNewProduct({ ...newProduct, [name]: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddProduct = async () => {
|
||||||
|
const { name, description, price, type, availability } = newProduct;
|
||||||
|
|
||||||
|
if (!name || !description || !price || type === '') {
|
||||||
|
setError('Proszę uzupełnić wszystkie wymagane pola.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
price,
|
||||||
|
type: parseInt(type, 10),
|
||||||
|
...(type === '1' && { availability: availability || 0 }),
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
if (!token) {
|
||||||
|
setError('Brak tokena. Użytkownik musi być zalogowany.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await axios.post('https://localhost:7039/api/Products', payload, config);
|
||||||
|
|
||||||
|
setNewProduct({
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
price: '',
|
||||||
|
type: '1',
|
||||||
|
availability: '',
|
||||||
|
});
|
||||||
|
setError(null);
|
||||||
|
navigate('/produkty');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Błąd podczas dodawania produktu:', error);
|
||||||
|
setError('Wystąpił błąd podczas dodawania produktu. Spróbuj ponownie.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-8 bg-gray-100 rounded-lg shadow-md">
|
||||||
|
<h2 className="text-2xl font-bold mb-4">Dodaj nowy produkt lub usługę</h2>
|
||||||
|
{error && <p className="text-red-500 mb-4">{error}</p>}
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
value={newProduct.name}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Nazwa produktu lub usługi"
|
||||||
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="description"
|
||||||
|
value={newProduct.description}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Opis"
|
||||||
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
name="price"
|
||||||
|
value={newProduct.price}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Cena"
|
||||||
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
<select
|
||||||
|
name="type"
|
||||||
|
value={newProduct.type}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
>
|
||||||
|
<option value="1">Produkt</option>
|
||||||
|
<option value="0">Usługa</option>
|
||||||
|
</select>
|
||||||
|
{newProduct.type === '1' && (
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="availability"
|
||||||
|
value={newProduct.availability}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Dostępność (ilość)"
|
||||||
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<button
|
||||||
|
onClick={handleAddProduct}
|
||||||
|
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
|
||||||
|
>
|
||||||
|
Dodaj
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DodawanieProduktu;
|
260
firm/src/components/DodawanieTransakcji.js
Normal file
260
firm/src/components/DodawanieTransakcji.js
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import Select from 'react-select';
|
||||||
|
|
||||||
|
const DodawanieTransakcji = () => {
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const [newTransaction, setNewTransaction] = useState({
|
||||||
|
id: 2,
|
||||||
|
date: '',
|
||||||
|
employeeId: '',
|
||||||
|
paymentType: '',
|
||||||
|
discount: '',
|
||||||
|
description: '',
|
||||||
|
transactionProducts: [
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
transactionId: 0,
|
||||||
|
productID: 0,
|
||||||
|
productName: '',
|
||||||
|
quantity: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const [products, setProducts] = useState([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchProducts = async () => {
|
||||||
|
try {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
if (!token) {
|
||||||
|
setError('Brak tokena. Użytkownik musi być zalogowany.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await axios.get('https://localhost:7039/api/Products', config);
|
||||||
|
const productOptions = response.data.map(product => ({
|
||||||
|
value: product.id,
|
||||||
|
label: product.name,
|
||||||
|
}));
|
||||||
|
setProducts(productOptions);
|
||||||
|
} catch (error) {
|
||||||
|
setError('Wystąpił błąd podczas ładowania produktów.');
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchProducts();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleInputChange = (event) => {
|
||||||
|
const { name, value } = event.target;
|
||||||
|
setNewTransaction({ ...newTransaction, [name]: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleProductChange = (index, selectedOption) => {
|
||||||
|
const updatedTransactionProducts = [...newTransaction.transactionProducts];
|
||||||
|
updatedTransactionProducts[index].productID = selectedOption.value;
|
||||||
|
updatedTransactionProducts[index].productName = selectedOption.label;
|
||||||
|
setNewTransaction({
|
||||||
|
...newTransaction,
|
||||||
|
transactionProducts: updatedTransactionProducts,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddProduct = () => {
|
||||||
|
setNewTransaction({
|
||||||
|
...newTransaction,
|
||||||
|
transactionProducts: [
|
||||||
|
...newTransaction.transactionProducts,
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
transactionId: 0,
|
||||||
|
productID: 0,
|
||||||
|
productName: '',
|
||||||
|
quantity: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveProduct = (index) => {
|
||||||
|
const updatedTransactionProducts = [...newTransaction.transactionProducts];
|
||||||
|
updatedTransactionProducts.splice(index, 1);
|
||||||
|
setNewTransaction({
|
||||||
|
...newTransaction,
|
||||||
|
transactionProducts: updatedTransactionProducts,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddTransaction = async () => {
|
||||||
|
if (!newTransaction.date || !newTransaction.employeeId || newTransaction.transactionProducts.some(product => !product.productName || !product.quantity)) {
|
||||||
|
setError('Proszę uzupełnić wszystkie pola.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('Nowa transakcja:', newTransaction);
|
||||||
|
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
if (!token) {
|
||||||
|
setError('Brak tokena. Użytkownik musi być zalogowany.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await axios.post('https://localhost:7039/api/Transaction', newTransaction, config);
|
||||||
|
|
||||||
|
setNewTransaction({
|
||||||
|
id: 0,
|
||||||
|
date: '',
|
||||||
|
employeeId: '',
|
||||||
|
transactionProducts: [
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
transactionId: 0,
|
||||||
|
productID: 0,
|
||||||
|
productName: '',
|
||||||
|
quantity: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
paymentType: '',
|
||||||
|
discount: '',
|
||||||
|
description: '',
|
||||||
|
totalPrice: 0,
|
||||||
|
});
|
||||||
|
navigate('/transakcje');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Błąd podczas dodawania transakcji:', error);
|
||||||
|
if (error.response && error.response.data) {
|
||||||
|
setError(error.response.data);
|
||||||
|
} else {
|
||||||
|
setError('Wystąpił nieoczekiwany błąd. Spróbuj ponownie później.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-white p-8 rounded-lg">
|
||||||
|
<h2 className="text-2xl font-bold mb-4">Dodaj nową transakcję</h2>
|
||||||
|
|
||||||
|
{error && <p className="text-red-500 mb-4">{error}</p>}
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="datetime-local"
|
||||||
|
name="date"
|
||||||
|
value={newTransaction.date}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Data"
|
||||||
|
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="employeeId"
|
||||||
|
value={newTransaction.employeeId}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Nr. Pracownika"
|
||||||
|
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="text-center">Ładowanie produktów...</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{newTransaction.transactionProducts.map((product, index) => (
|
||||||
|
<div key={index} className="mb-4">
|
||||||
|
<Select
|
||||||
|
name={`productName-${index}`}
|
||||||
|
value={products.find(option => option.value === product.productID)}
|
||||||
|
onChange={(selectedOption) => handleProductChange(index, selectedOption)}
|
||||||
|
options={products}
|
||||||
|
className="block w-full mb-2"
|
||||||
|
placeholder="Wybierz produkt..."
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name={`quantity-${index}`}
|
||||||
|
value={product.quantity}
|
||||||
|
onChange={(e) => {
|
||||||
|
const updatedTransactionProducts = [...newTransaction.transactionProducts];
|
||||||
|
updatedTransactionProducts[index].quantity = e.target.value;
|
||||||
|
setNewTransaction({ ...newTransaction, transactionProducts: updatedTransactionProducts });
|
||||||
|
}}
|
||||||
|
placeholder="Ilość"
|
||||||
|
className="block w-full mb-2 px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => handleRemoveProduct(index)}
|
||||||
|
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
|
||||||
|
>
|
||||||
|
Usuń
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleAddProduct}
|
||||||
|
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 my-3 rounded"
|
||||||
|
>
|
||||||
|
Dodaj produkt
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="paymentType"
|
||||||
|
value={newTransaction.paymentType}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Sposób płatności"
|
||||||
|
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="discount"
|
||||||
|
value={newTransaction.discount}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Rabat"
|
||||||
|
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="description"
|
||||||
|
value={newTransaction.description}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Opis"
|
||||||
|
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleAddTransaction}
|
||||||
|
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 my-3 rounded"
|
||||||
|
>
|
||||||
|
Dodaj transakcję
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DodawanieTransakcji;
|
157
firm/src/components/EdycjaProduktu.js
Normal file
157
firm/src/components/EdycjaProduktu.js
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
const EdycjaProduktu = () => {
|
||||||
|
const { id } = useParams();
|
||||||
|
const [product, setProduct] = useState({
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
price: '',
|
||||||
|
type: '1',
|
||||||
|
availability: '',
|
||||||
|
});
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchProduct = async () => {
|
||||||
|
try {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
if (!token) {
|
||||||
|
setError('Brak tokena. Użytkownik musi być zalogowany.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios.get(`https://localhost:7039/api/Products/${id}`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setProduct(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Błąd podczas pobierania produktu:', error);
|
||||||
|
setError('Wystąpił błąd podczas pobierania danych produktu.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchProduct();
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
const handleInputChange = (event) => {
|
||||||
|
const { name, value } = event.target;
|
||||||
|
setProduct({ ...product, [name]: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveChanges = async () => {
|
||||||
|
const { name, description, price, type, availability } = product;
|
||||||
|
|
||||||
|
if (!name || !description || !price || type === '') {
|
||||||
|
setError('Proszę uzupełnić wszystkie wymagane pola.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
price,
|
||||||
|
type: parseInt(type, 10),
|
||||||
|
...(type === '1' && { availability: availability || 0 }),
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
if (!token) {
|
||||||
|
setError('Brak tokena. Użytkownik musi być zalogowany.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await axios.put(`https://localhost:7039/api/Products/${id}`, payload, config);
|
||||||
|
|
||||||
|
console.log('Produkt zapisany:', response.data);
|
||||||
|
|
||||||
|
setError(null);
|
||||||
|
navigate('/produkty');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Błąd podczas zapisywania zmian:', error);
|
||||||
|
|
||||||
|
if (error.response && error.response.status === 400) {
|
||||||
|
setError('ID produktu nie zgadza się.');
|
||||||
|
} else {
|
||||||
|
setError('Wystąpił błąd podczas zapisywania zmian. Spróbuj ponownie.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-8 bg-gray-100 rounded-lg shadow-md">
|
||||||
|
<h2 className="text-2xl font-bold mb-4">Edycja produktu</h2>
|
||||||
|
{error && <p className="text-red-500 mb-4">{error}</p>}
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
value={product.name}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Nazwa produktu lub usługi"
|
||||||
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="description"
|
||||||
|
value={product.description}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Opis"
|
||||||
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
name="price"
|
||||||
|
value={product.price}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Cena"
|
||||||
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
<select
|
||||||
|
name="type"
|
||||||
|
value={product.type}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
>
|
||||||
|
<option value="1">Produkt</option>
|
||||||
|
<option value="0">Usługa</option>
|
||||||
|
</select>
|
||||||
|
{product.type === '1' && (
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="availability"
|
||||||
|
value={product.availability}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Dostępność (ilość)"
|
||||||
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<button
|
||||||
|
onClick={handleSaveChanges}
|
||||||
|
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
|
||||||
|
>
|
||||||
|
Zapisz zmiany
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EdycjaProduktu;
|
243
firm/src/components/EdycjaTransakcji.js
Normal file
243
firm/src/components/EdycjaTransakcji.js
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
|
import Select from 'react-select';
|
||||||
|
|
||||||
|
const EdycjaTransakcji = () => {
|
||||||
|
const { id } = useParams();
|
||||||
|
const [transaction, setTransaction] = useState({
|
||||||
|
date: '',
|
||||||
|
employeeId: '',
|
||||||
|
paymentType: '',
|
||||||
|
discount: '',
|
||||||
|
description: '',
|
||||||
|
transactionProducts: [{ productID: '', productName: '', quantity: '' }],
|
||||||
|
});
|
||||||
|
const [products, setProducts] = useState([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const getToken = () => {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
if (!token) {
|
||||||
|
setError('Brak tokena. Użytkownik musi być zalogowany.');
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchTransaction = async () => {
|
||||||
|
try {
|
||||||
|
const token = getToken();
|
||||||
|
if (!token) return;
|
||||||
|
|
||||||
|
const transactionResponse = await axios.get(`https://localhost:7039/api/transaction/${id}`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log('Dane transakcji:', transactionResponse.data);
|
||||||
|
|
||||||
|
const updatedTransaction = {
|
||||||
|
...transactionResponse.data,
|
||||||
|
transactionProducts: transactionResponse.data.transactionProducts.map((transactionProduct) => ({
|
||||||
|
productID: transactionProduct.product.id,
|
||||||
|
productName: transactionProduct.product.name,
|
||||||
|
quantity: transactionProduct.quantity,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
setTransaction(updatedTransaction);
|
||||||
|
|
||||||
|
const productResponse = await axios.get('https://localhost:7039/api/Products', {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log('Produkty:', productResponse.data);
|
||||||
|
|
||||||
|
const productOptions = productResponse.data.map((product) => ({
|
||||||
|
value: product.id,
|
||||||
|
label: product.name,
|
||||||
|
}));
|
||||||
|
setProducts(productOptions);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Błąd podczas pobierania transakcji lub produktów:', error);
|
||||||
|
setError('Wystąpił błąd podczas ładowania danych.');
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchTransaction();
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const handleInputChange = (event) => {
|
||||||
|
const { name, value } = event.target;
|
||||||
|
setTransaction({ ...transaction, [name]: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleProductChange = (index, selectedOption) => {
|
||||||
|
const updatedTransactionProducts = [...transaction.transactionProducts];
|
||||||
|
updatedTransactionProducts[index].productID = selectedOption.value;
|
||||||
|
updatedTransactionProducts[index].productName = selectedOption.label;
|
||||||
|
setTransaction({
|
||||||
|
...transaction,
|
||||||
|
transactionProducts: updatedTransactionProducts,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const handleAddProduct = () => {
|
||||||
|
setTransaction({
|
||||||
|
...transaction,
|
||||||
|
transactionProducts: [
|
||||||
|
...transaction.transactionProducts,
|
||||||
|
{ productID: 0, productName: '', quantity: '' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveProduct = (index) => {
|
||||||
|
const updatedTransactionProducts = [...transaction.transactionProducts];
|
||||||
|
updatedTransactionProducts.splice(index, 1);
|
||||||
|
setTransaction({
|
||||||
|
...transaction,
|
||||||
|
transactionProducts: updatedTransactionProducts,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveChanges = async () => {
|
||||||
|
if (!transaction.date || !transaction.employeeId || transaction.transactionProducts.some(product => !product.productName || !product.quantity)) {
|
||||||
|
setError('Proszę uzupełnić wszystkie pola.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const token = getToken();
|
||||||
|
if (!token) {
|
||||||
|
setError('Brak tokena. Użytkownik musi być zalogowany.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios.put(`https://localhost:7039/api/transaction/${id}`, transaction, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log('Zaktualizowana transakcja:', response.data);
|
||||||
|
navigate('/transakcje');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Błąd podczas zapisywania zmian:', error);
|
||||||
|
setError('Wystąpił błąd podczas zapisywania zmian.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-white p-8 rounded-lg">
|
||||||
|
<h2 className="text-2xl font-bold mb-4">Edycja Transakcji</h2>
|
||||||
|
|
||||||
|
{error && <p className="text-red-500 mb-4">{error}</p>}
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="datetime-local"
|
||||||
|
name="date"
|
||||||
|
value={transaction.date}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Data"
|
||||||
|
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="employeeId"
|
||||||
|
value={transaction.employeeId}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Nr. Pracownika"
|
||||||
|
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="text-center">Ładowanie produktów...</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{transaction.transactionProducts.map((product, index) => (
|
||||||
|
<div key={index} className="mb-4">
|
||||||
|
<Select
|
||||||
|
name={`productName-${index}`}
|
||||||
|
value={products.find(option => option.value === product.productID) || null}
|
||||||
|
onChange={(selectedOption) => handleProductChange(index, selectedOption)}
|
||||||
|
options={products}
|
||||||
|
className="block w-full mb-2"
|
||||||
|
placeholder="Wybierz produkt..."
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name={`quantity-${index}`}
|
||||||
|
value={product.quantity}
|
||||||
|
onChange={(e) => {
|
||||||
|
const updatedTransactionProducts = [...transaction.transactionProducts];
|
||||||
|
updatedTransactionProducts[index].quantity = e.target.value;
|
||||||
|
setTransaction({ ...transaction, transactionProducts: updatedTransactionProducts });
|
||||||
|
}}
|
||||||
|
placeholder="Ilość"
|
||||||
|
className="block w-full mb-2 px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => handleRemoveProduct(index)}
|
||||||
|
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
|
||||||
|
>
|
||||||
|
Usuń
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleAddProduct}
|
||||||
|
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 my-3 rounded"
|
||||||
|
>
|
||||||
|
Dodaj produkt
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="paymentType"
|
||||||
|
value={transaction.paymentType}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Sposób płatności"
|
||||||
|
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="discount"
|
||||||
|
value={transaction.discount}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Rabat"
|
||||||
|
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="description"
|
||||||
|
value={transaction.description}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Opis"
|
||||||
|
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleSaveChanges}
|
||||||
|
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 my-3 rounded"
|
||||||
|
>
|
||||||
|
Zapisz zmiany
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EdycjaTransakcji;
|
135
firm/src/components/ListaProduktów.js
Normal file
135
firm/src/components/ListaProduktów.js
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import editIcon from '../icons/edit.png';
|
||||||
|
import koszIcon from '../icons/kosz.png';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
const ListaProduktow = ({ onAdd }) => {
|
||||||
|
const [products, setProducts] = useState([]);
|
||||||
|
const [deleteProductId, setDeleteProductId] = useState(null);
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const fetchProducts = async () => {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
try {
|
||||||
|
const response = await axios.get('https://localhost:7039/api/products', {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
setProducts(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Błąd podczas pobierania produktów:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchProducts();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleDeleteProduct = async () => {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
if (!token) {
|
||||||
|
alert('Brak tokena. Użytkownik musi być zalogowany.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deleteProductId) {
|
||||||
|
console.error('Brak ID produktu do usunięcia!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.delete(`https://localhost:7039/api/Products/${deleteProductId}`, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
fetchProducts();
|
||||||
|
setShowModal(false);
|
||||||
|
setDeleteProductId(null);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Błąd podczas usuwania produktu:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEditProduct = (productId) => {
|
||||||
|
navigate(`/produkty/edytuj/${productId}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h1 className="text-5xl">Katalog Produktów</h1>
|
||||||
|
<button onClick={onAdd} className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded">
|
||||||
|
Dodaj Produkt
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5">
|
||||||
|
<table className="w-full border border-gray-300">
|
||||||
|
<thead className="bg-gray-200">
|
||||||
|
<tr>
|
||||||
|
<th className="p-2 border">ID</th>
|
||||||
|
<th className="p-2 border">Produkt</th>
|
||||||
|
<th className="p-2 border">Opis</th>
|
||||||
|
<th className="p-2 border">Cena</th>
|
||||||
|
<th className="p-2 border">Dostępność</th>
|
||||||
|
<th className="p-2 border"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{products.map((product) => (
|
||||||
|
<tr key={product.id}>
|
||||||
|
<td className="p-2 border">{product.id}</td>
|
||||||
|
<td className="p-2 border">{product.name}</td>
|
||||||
|
<td className="p-2 border">{product.description}</td>
|
||||||
|
<td className="p-2 border">{parseFloat(product.price).toFixed(2)}</td>
|
||||||
|
<td className="p-2 border">{product.availability}</td>
|
||||||
|
<td className="p-2 border">
|
||||||
|
<button
|
||||||
|
onClick={() => handleEditProduct(product.id)}
|
||||||
|
className="mr-2 bg-blue-500 text-white py-2 px-4 rounded"
|
||||||
|
>
|
||||||
|
<img src={editIcon} alt="Edytuj" className="inline w-5 mr-2" />
|
||||||
|
Edytuj
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setDeleteProductId(product.id);
|
||||||
|
setShowModal(true);
|
||||||
|
}}
|
||||||
|
className="bg-red-500 text-white py-2 px-4 rounded"
|
||||||
|
>
|
||||||
|
<img src={koszIcon} alt="Usuń" className="inline w-5 mr-2" />
|
||||||
|
Usuń
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{showModal && (
|
||||||
|
<div className="fixed inset-0 bg-gray-500 bg-opacity-50 flex justify-center items-center z-50">
|
||||||
|
<div className="bg-white p-6 rounded-md shadow-lg w-96">
|
||||||
|
<h2 className="text-lg font-bold mb-4">Czy na pewno chcesz usunąć ten produkt?</h2>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<button
|
||||||
|
onClick={handleDeleteProduct}
|
||||||
|
className="bg-red-500 text-white py-2 px-4 rounded"
|
||||||
|
>
|
||||||
|
Tak
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowModal(false)}
|
||||||
|
className="bg-gray-500 text-white py-2 px-4 rounded"
|
||||||
|
>
|
||||||
|
Anuluj
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ListaProduktow;
|
177
firm/src/components/ListaTransakcji.js
Normal file
177
firm/src/components/ListaTransakcji.js
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import editIcon from '../icons/edit.png';
|
||||||
|
import koszIcon from '../icons/kosz.png';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
const ListaTransakcji = ({ onAdd}) => {
|
||||||
|
const [transactions, setTransactions] = useState([]);
|
||||||
|
const [deleteTransactionId, setDeleteTransactionId] = useState(null);
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const fetchTransactions = async () => {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
if (!token) {
|
||||||
|
console.error('Brak tokena. Użytkownik musi być zalogowany.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await axios.get('https://localhost:7039/api/Transaction', {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setTransactions(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Błąd podczas pobierania transakcji:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchProducts = async () => {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
if (!token) {
|
||||||
|
console.error('Brak tokena. Użytkownik musi być zalogowany.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await axios.get('https://localhost:7039/api/Products', {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
response.data.map(product => ({ value: product.id, label: product.name }));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Błąd podczas pobierania produktów:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchTransactions();
|
||||||
|
fetchProducts();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const formatDate = (date) => {
|
||||||
|
const newDate = new Date(date);
|
||||||
|
return newDate.toLocaleDateString();
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatPrice = (price) => {
|
||||||
|
return price.toFixed(2).replace('.', ',');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteTransaction = async () => {
|
||||||
|
try {
|
||||||
|
await axios.delete(`https://localhost:7039/api/transaction/${deleteTransactionId}`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${localStorage.getItem('token')}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setTransactions(transactions.filter(transaction => transaction.id !== deleteTransactionId));
|
||||||
|
setShowModal(false);
|
||||||
|
setDeleteTransactionId(null);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Błąd podczas usuwania transakcji:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const openDeleteConfirmation = (transactionId) => {
|
||||||
|
setDeleteTransactionId(transactionId);
|
||||||
|
setShowModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEditTransaction = (transactionId) => {
|
||||||
|
navigate(`/transakcje/edytuj/${transactionId}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h1 className="text-5xl">Lista Transakcji</h1>
|
||||||
|
<button onClick={onAdd} className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded">
|
||||||
|
Dodaj Transakcję
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="w-8/10 mx-auto mt-2">
|
||||||
|
<div className="h-screen overflow-y-auto">
|
||||||
|
<table className="w-full border-collapse border border-gray-300">
|
||||||
|
<thead className="bg-gray-200">
|
||||||
|
<tr>
|
||||||
|
<th className="border border-gray-300 p-2">ID</th>
|
||||||
|
<th className="border border-gray-300 p-2">Data</th>
|
||||||
|
<th className="border border-gray-300 p-2">Produkt</th>
|
||||||
|
<th className="border border-gray-300 p-2">Ilość</th>
|
||||||
|
<th className="border border-gray-300 p-2">Kwota</th>
|
||||||
|
<th className="border border-gray-300 p-2">Sposób płatności</th>
|
||||||
|
<th className="border border-gray-300 p-2">Nr. Pracownika</th>
|
||||||
|
<th className="border border-gray-300 p-2"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{transactions.map(transaction => (
|
||||||
|
<tr key={transaction.id}>
|
||||||
|
<td className="border border-gray-300 p-2">{transaction.id}</td>
|
||||||
|
<td className="border border-gray-300 p-2">{formatDate(transaction.date)}</td>
|
||||||
|
<td className="border border-gray-300 p-2">
|
||||||
|
{transaction.transactionProducts.map(product => (
|
||||||
|
<div key={product.id}>{product.product.name}</div>
|
||||||
|
))}
|
||||||
|
</td>
|
||||||
|
<td className="border border-gray-300 p-2">
|
||||||
|
{transaction.transactionProducts.map(product => (
|
||||||
|
<div key={product.id}>{product.quantity}</div>
|
||||||
|
))}
|
||||||
|
</td>
|
||||||
|
<td className="border border-gray-300 p-2">{formatPrice(transaction.totalPrice)}</td>
|
||||||
|
<td className="border border-gray-300 p-2">{transaction.paymentType}</td>
|
||||||
|
<td className="border border-gray-300 p-2">{transaction.employeeId}</td>
|
||||||
|
<td className="p-2 border">
|
||||||
|
<button
|
||||||
|
onClick={() => handleEditTransaction(transaction.id)}
|
||||||
|
className="mr-2 bg-blue-500 text-white py-2 px-4 rounded"
|
||||||
|
>
|
||||||
|
<img src={editIcon} alt="Edytuj" className="inline w-5 mr-2" />
|
||||||
|
Edytuj
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => openDeleteConfirmation(transaction.id)}
|
||||||
|
className="bg-red-500 text-white py-2 px-4 rounded"
|
||||||
|
>
|
||||||
|
<img src={koszIcon} alt="Usuń" className="inline w-5 mr-2" />
|
||||||
|
Usuń
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{showModal && (
|
||||||
|
<div className="fixed inset-0 bg-gray-500 bg-opacity-50 flex justify-center items-center z-50">
|
||||||
|
<div className="bg-white p-6 rounded-md shadow-lg w-96">
|
||||||
|
<h2 className="text-lg font-bold mb-4">Czy na pewno chcesz usunąć tę transakcję?</h2>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<button
|
||||||
|
onClick={handleDeleteTransaction}
|
||||||
|
className="bg-red-500 text-white py-2 px-4 rounded"
|
||||||
|
>
|
||||||
|
Tak
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowModal(false)}
|
||||||
|
className="bg-gray-500 text-white py-2 px-4 rounded"
|
||||||
|
>
|
||||||
|
Anuluj
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ListaTransakcji;
|
@ -1,488 +0,0 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import axios from 'axios';
|
|
||||||
import Select from 'react-select';
|
|
||||||
import editIcon from "../icons/edit.png";
|
|
||||||
import koszIcon from "../icons/kosz.png";
|
|
||||||
import plusIcon from "../icons/plus.png";
|
|
||||||
import ConfirmationModal from './ConfirmationModal';
|
|
||||||
|
|
||||||
const Transakcje = () => {
|
|
||||||
const [transactions, setTransactions] = useState([]);
|
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
||||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
|
||||||
const [editTransaction, setEditTransaction] = useState(null);
|
|
||||||
const [error, setError] = useState(null);
|
|
||||||
const [products, setProducts] = useState([]);
|
|
||||||
const [deleteTransactionId, setDeleteTransactionId] = useState(null);
|
|
||||||
const [newTransaction, setNewTransaction] = useState({
|
|
||||||
id: 2,
|
|
||||||
date: "",
|
|
||||||
employeeId: "",
|
|
||||||
transactionProducts: [
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
transactionId: 2,
|
|
||||||
productID: 0,
|
|
||||||
productName: "",
|
|
||||||
quantity: ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
paymentType: "",
|
|
||||||
discount: "",
|
|
||||||
description: "",
|
|
||||||
totalPrice: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
const fetchTransactions = async () => {
|
|
||||||
const token = localStorage.getItem('token');
|
|
||||||
if (!token) {
|
|
||||||
console.error('Brak tokena. Użytkownik musi być zalogowany.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const response = await axios.get('https://localhost:7039/api/Transaction', {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setTransactions(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Błąd podczas dodawania transakcji:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const fetchProducts = async () => {
|
|
||||||
const token = localStorage.getItem('token');
|
|
||||||
if (!token) {
|
|
||||||
console.error('Brak tokena. Użytkownik musi być zalogowany.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const response = await axios.get('https://localhost:7039/api/Products', {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setProducts(response.data.map(product => ({ value: product.id, label: product.name })));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Błąd podczas pobierania produktów:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchTransactions();
|
|
||||||
fetchProducts();
|
|
||||||
console.log();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleAddTransaction = async () => {
|
|
||||||
if (!newTransaction.date || !newTransaction.employeeId || newTransaction.transactionProducts.some(product => !product.productName || !product.quantity)) {
|
|
||||||
setError('Proszę uzupełnić wszystkie pola.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
console.log('Nowa transakcja:', newTransaction);
|
|
||||||
await axios.post('https://localhost:7039/api/Transaction', newTransaction);
|
|
||||||
|
|
||||||
fetchTransactions();
|
|
||||||
setIsModalOpen(false);
|
|
||||||
setNewTransaction({
|
|
||||||
id: 2,
|
|
||||||
date: "",
|
|
||||||
employeeId: "",
|
|
||||||
transactionProducts: [
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
transactionId: 0,
|
|
||||||
productID: 0,
|
|
||||||
productName: "",
|
|
||||||
quantity: ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
paymentType: "",
|
|
||||||
discount: "",
|
|
||||||
description: "",
|
|
||||||
totalPrice: 0
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Błąd podczas dodawania transakcji:', error);
|
|
||||||
if (error.response && error.response.data) {
|
|
||||||
setError(error.response.data);
|
|
||||||
} else {
|
|
||||||
setError('Wystąpił nieoczekiwany błąd. Spróbuj ponownie później.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatDate = (dateString) => {
|
|
||||||
const options = { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' };
|
|
||||||
const date = new Date(dateString);
|
|
||||||
return date.toLocaleDateString('pl-PL', options).replace(",", "");
|
|
||||||
};
|
|
||||||
const formatPrice = (price) => {
|
|
||||||
return parseFloat(price).toFixed(2);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAddProduct = () => {
|
|
||||||
setNewTransaction({
|
|
||||||
...newTransaction,
|
|
||||||
transactionProducts: [
|
|
||||||
...newTransaction.transactionProducts,
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
transactionId: 0,
|
|
||||||
productID: 0,
|
|
||||||
productName: "",
|
|
||||||
quantity: ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRemoveProduct = (index) => {
|
|
||||||
const updatedTransactionProducts = [...newTransaction.transactionProducts];
|
|
||||||
updatedTransactionProducts.splice(index, 1);
|
|
||||||
setNewTransaction({
|
|
||||||
...newTransaction,
|
|
||||||
transactionProducts: updatedTransactionProducts
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteTransaction = async (transactionId) => {
|
|
||||||
try {
|
|
||||||
await axios.delete(`https://localhost:7039/api/Transaction/${transactionId}`);
|
|
||||||
fetchTransactions();
|
|
||||||
setDeleteTransactionId(null);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Błąd podczas usuwania transakcji:', error);
|
|
||||||
if (error.response && error.response.data) {
|
|
||||||
setError(error.response.data);
|
|
||||||
} else {
|
|
||||||
setError('Wystąpił nieoczekiwany błąd. Spróbuj ponownie później.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handleEditTransaction = async (transaction) => {
|
|
||||||
try {
|
|
||||||
if (!editTransaction) {
|
|
||||||
setEditTransaction(transaction);
|
|
||||||
setIsEditModalOpen(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!editTransaction.date || !editTransaction.employeeId || editTransaction.transactionProducts.some(product => !product.productName || !product.quantity) || !editTransaction.paymentType){
|
|
||||||
setError('Proszę uzupełnić wszystkie pola.');
|
|
||||||
return;}
|
|
||||||
await axios.put(`https://localhost:7039/api/Transaction/${editTransaction.id}`, editTransaction);
|
|
||||||
fetchTransactions();
|
|
||||||
setIsEditModalOpen(false);
|
|
||||||
setEditTransaction(null);
|
|
||||||
setError(null);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Błąd podczas edycji transakcji:', error);
|
|
||||||
if (error.response && error.response.data) {
|
|
||||||
setError(error.response.data);
|
|
||||||
} else {
|
|
||||||
setError('Wystąpił nieoczekiwany błąd. Spróbuj ponownie później.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handleProductChange = (index, selectedOption) => {
|
|
||||||
const updatedTransactionProducts = [...newTransaction.transactionProducts];
|
|
||||||
updatedTransactionProducts[index].productID = selectedOption.value;
|
|
||||||
updatedTransactionProducts[index].productName = selectedOption.label;
|
|
||||||
setNewTransaction({
|
|
||||||
...newTransaction,
|
|
||||||
transactionProducts: updatedTransactionProducts
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const handleEditProductChange = (index, selectedOption) => {
|
|
||||||
const updatedTransactionProducts = [...editTransaction.transactionProducts];
|
|
||||||
updatedTransactionProducts[index].productID = selectedOption.value;
|
|
||||||
updatedTransactionProducts[index].productName = selectedOption.label;
|
|
||||||
setEditTransaction({
|
|
||||||
...editTransaction,
|
|
||||||
transactionProducts: updatedTransactionProducts
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const openDeleteConfirmation = (transactionId) => {
|
|
||||||
setDeleteTransactionId(transactionId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeDeleteConfirmation = () => {
|
|
||||||
setDeleteTransactionId(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='p-10 ml-11'>
|
|
||||||
<div className='flex items-center justify-between'>
|
|
||||||
<div className='h-20 text-5xl ml-1'>
|
|
||||||
Lista Transakcji
|
|
||||||
</div>
|
|
||||||
<button onClick={() => setIsModalOpen(true)} className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded flex">
|
|
||||||
<img src={plusIcon} alt="" className="w-8 h-8 mr-2" />Dodaj
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{error && (
|
|
||||||
<div className="absolute top-0 left-0 w-full h-full bg-black bg-opacity-50 flex items-center justify-center">
|
|
||||||
<div className="bg-white p-8 rounded-lg">
|
|
||||||
<h2 className="text-2xl font-bold mb-4">Błąd</h2>
|
|
||||||
<p>{error}</p>
|
|
||||||
<button onClick={() => window.location.reload()} className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">
|
|
||||||
Zamknij
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isEditModalOpen && editTransaction && (
|
|
||||||
<div className="absolute top-0 left-0 w-full h-screen bg-black bg-opacity-50 flex items-center justify-center">
|
|
||||||
<div className="bg-white p-8 rounded-lg">
|
|
||||||
<h2 className="text-2xl font-bold mb-4">Edytuj transakcję</h2>
|
|
||||||
<input
|
|
||||||
type="datetime-local"
|
|
||||||
name="date"
|
|
||||||
value={editTransaction.date}
|
|
||||||
onChange={(e) => setEditTransaction({ ...editTransaction, date: e.target.value })}
|
|
||||||
placeholder="Data"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
Numer Pracownika
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name="employeeId"
|
|
||||||
value={editTransaction.employeeId}
|
|
||||||
onChange={(e) => setEditTransaction({ ...editTransaction, employeeId: e.target.value })}
|
|
||||||
placeholder="Nr. Pracownika"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
{editTransaction.transactionProducts.map((product, index) => (
|
|
||||||
<div key={index}>
|
|
||||||
<Select
|
|
||||||
name={`productName-${index}`}
|
|
||||||
value={products.find(option => option.value === product.productName)}
|
|
||||||
onChange={(selectedOption) => handleEditProductChange(index, selectedOption)}
|
|
||||||
options={products}
|
|
||||||
className="block w-full mb-4"
|
|
||||||
placeholder="Wybierz produkt..."
|
|
||||||
/>
|
|
||||||
Ilość
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name={`quantity-${index}`}
|
|
||||||
value={product.quantity}
|
|
||||||
onChange={(e) => {
|
|
||||||
const newTransactionProducts = [...editTransaction.transactionProducts];
|
|
||||||
newTransactionProducts[index].quantity = e.target.value;
|
|
||||||
setEditTransaction({ ...editTransaction, transactionProducts: newTransactionProducts });
|
|
||||||
}}
|
|
||||||
placeholder="Ilość"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="paymentType"
|
|
||||||
value={editTransaction.paymentType}
|
|
||||||
onChange={(e) => setEditTransaction({ ...editTransaction, paymentType: e.target.value })}
|
|
||||||
placeholder="Sposób płatności"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
Zniżka
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name="discount"
|
|
||||||
value={editTransaction.discount}
|
|
||||||
onChange={(e) => setEditTransaction({ ...editTransaction, discount: e.target.value })}
|
|
||||||
placeholder="Rabat"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="description"
|
|
||||||
value={editTransaction.description}
|
|
||||||
onChange={(e) => setEditTransaction({ ...editTransaction, description: e.target.value })}
|
|
||||||
placeholder="Opis"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
handleEditTransaction();
|
|
||||||
setIsEditModalOpen(false);
|
|
||||||
}}
|
|
||||||
className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
|
|
||||||
>
|
|
||||||
Zapisz zmiany
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => window.location.reload()}
|
|
||||||
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
|
|
||||||
>
|
|
||||||
Anuluj
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isModalOpen && (
|
|
||||||
<div className="absolute top-0 left-0 w-full h-full bg-black bg-opacity-50 flex items-center justify-center">
|
|
||||||
<div className="bg-white p-8 rounded-lg">
|
|
||||||
<h2 className="text-2xl font-bold mb-4">Dodaj nową transakcję</h2>
|
|
||||||
<input
|
|
||||||
type="datetime-local"
|
|
||||||
name="date"
|
|
||||||
value={newTransaction.date}
|
|
||||||
onChange={(e) => setNewTransaction({ ...newTransaction, date: e.target.value })}
|
|
||||||
placeholder="Data"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name="employeeId"
|
|
||||||
value={newTransaction.employeeId}
|
|
||||||
onChange={(e) => setNewTransaction({ ...newTransaction, employeeId: e.target.value })}
|
|
||||||
placeholder="Nr. Pracownika"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
{newTransaction.transactionProducts.map((product, index) => (
|
|
||||||
<div key={index}>
|
|
||||||
<Select
|
|
||||||
name={`productName-${index}`}
|
|
||||||
value={products.find(option => option.value === product.productName)}
|
|
||||||
onChange={(selectedOption) => handleProductChange(index, selectedOption)}
|
|
||||||
options={products}
|
|
||||||
className="block w-full mb-4"
|
|
||||||
placeholder="Wybierz produkt..."
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name={`quantity-${index}`}
|
|
||||||
value={product.quantity}
|
|
||||||
onChange={(e) => {
|
|
||||||
const newTransactionProducts = [...newTransaction.transactionProducts];
|
|
||||||
newTransactionProducts[index].quantity = e.target.value;
|
|
||||||
setNewTransaction({ ...newTransaction, transactionProducts: newTransactionProducts });
|
|
||||||
}}
|
|
||||||
placeholder="Ilość" className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<button onClick={() => handleRemoveProduct(index)} className="bg-red-500 hover:bg-red-700 text-white font-bold my-2 py-2 px-4 rounded">
|
|
||||||
Usuń
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<button onClick={handleAddProduct} className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 my-3 rounded">
|
|
||||||
Dodaj
|
|
||||||
</button>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="paymentType"
|
|
||||||
value={newTransaction.paymentType}
|
|
||||||
onChange={(e) => setNewTransaction({ ...newTransaction, paymentType: e.target.value })}
|
|
||||||
placeholder="Sposób płatności"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name="discount"
|
|
||||||
value={newTransaction.discount}
|
|
||||||
onChange={(e) => setNewTransaction({ ...newTransaction, discount: e.target.value })}
|
|
||||||
placeholder="Rabat"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="description"
|
|
||||||
value={newTransaction.description}
|
|
||||||
onChange={(e) => setNewTransaction({ ...newTransaction, description: e.target.value })}
|
|
||||||
placeholder="Opis"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
handleAddTransaction();
|
|
||||||
setIsModalOpen(false);
|
|
||||||
|
|
||||||
}}
|
|
||||||
className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
|
|
||||||
>
|
|
||||||
Dodaj transakcję
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setIsModalOpen(false)}
|
|
||||||
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 mx-1 rounded"
|
|
||||||
>
|
|
||||||
Anuluj
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="w-8/10 mx-auto mt-2">
|
|
||||||
<div className="h-screen overflow-y-auto">
|
|
||||||
<table className="w-full border-collapse border border-gray-300">
|
|
||||||
<thead className="bg-gray-200">
|
|
||||||
<tr>
|
|
||||||
<th className="border border-gray-300 p-2">ID</th>
|
|
||||||
<th className="border border-gray-300 p-2">Data</th>
|
|
||||||
<th className="border border-gray-300 p-2">Produkt</th>
|
|
||||||
<th className="border border-gray-300 p-2">Ilość</th>
|
|
||||||
<th className="border border-gray-300 p-2">Kwota</th>
|
|
||||||
<th className="border border-gray-300 p-2">Sposób płatności</th>
|
|
||||||
<th className="border border-gray-300 p-2">Nr. Pracownika</th>
|
|
||||||
<th className="border border-gray-300 p-2"></th>
|
|
||||||
<th className="border border-gray-300 p-2"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{transactions.map(transaction => (
|
|
||||||
<tr key={transaction.id}>
|
|
||||||
<td className="border border-gray-300 p-2">{transaction.id}</td>
|
|
||||||
<td className="border border-gray-300 p-2">{formatDate(transaction.date)}</td>
|
|
||||||
<td className="border border-gray-300 p-2">
|
|
||||||
{transaction.transactionProducts.map(product => (
|
|
||||||
<div key={product.id}>{product.product.name}</div>
|
|
||||||
))}
|
|
||||||
</td>
|
|
||||||
<td className="border border-gray-300 p-2">
|
|
||||||
{transaction.transactionProducts.map(product => (
|
|
||||||
<div key={product.id}>{product.quantity}</div>
|
|
||||||
))}
|
|
||||||
</td>
|
|
||||||
<td className="border border-gray-300 p-2">{formatPrice(transaction.totalPrice)}</td>
|
|
||||||
<td className="border border-gray-300 p-2">{transaction.paymentType}</td>
|
|
||||||
<td className="border border-gray-300 p-2">{transaction.employeeId}</td>
|
|
||||||
<td className="border border-gray-300 p-2"><button onClick={() =>{handleEditTransaction(transaction);
|
|
||||||
console.log(transaction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
className="mr-2 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded flex">
|
|
||||||
<img src={editIcon} alt="" className="w-8 h-8 mr-2" />Edytuj
|
|
||||||
</button></td>
|
|
||||||
<td className="border border-gray-300 p-2"><button onClick={() => openDeleteConfirmation(transaction.id)}
|
|
||||||
className="mr-2 bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded flex">
|
|
||||||
<img src={koszIcon} alt="" className="w-8 h-8 mr-2" />Usuń
|
|
||||||
</button></td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-start mt-4">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{deleteTransactionId && (
|
|
||||||
<ConfirmationModal
|
|
||||||
message="Czy na pewno chcesz usunąć tę transakcję?"
|
|
||||||
onCancel={closeDeleteConfirmation}
|
|
||||||
onConfirm={() => {handleDeleteTransaction(deleteTransactionId); setDeleteTransactionId(false);}}
|
|
||||||
/>)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Transakcje
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,325 +1,22 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React from 'react';
|
||||||
import axios from 'axios';
|
import { Routes, Route, useNavigate } from 'react-router-dom';
|
||||||
import editIcon from "../icons/edit.png";
|
import ListaProduktow from './ListaProduktów';
|
||||||
import koszIcon from "../icons/kosz.png";
|
import DodawanieProduktu from './DodawanieProduktów';
|
||||||
import plusIcon from "../icons/plus.png";
|
import EdycjaProduktu from './EdycjaProduktu';
|
||||||
import ConfirmationModal from './ConfirmationModal';
|
|
||||||
|
|
||||||
const ZarzadzanieProduktami = () => {
|
const ZarzadzanieProduktami = () => {
|
||||||
const [products, setProducts] = useState([]);
|
const navigate = useNavigate();
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
||||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
|
||||||
const [error, setError] = useState(null);
|
|
||||||
const [deleteProductId, setDeleteProductId] = useState(null);
|
|
||||||
const [newProduct, setNewProduct] = useState({
|
|
||||||
name: "",
|
|
||||||
description: "",
|
|
||||||
price: "",
|
|
||||||
type: "",
|
|
||||||
availability: ""
|
|
||||||
});
|
|
||||||
const [editProduct, setEditProduct] = useState(null);
|
|
||||||
|
|
||||||
const fetchProducts = async () => {
|
|
||||||
const token = localStorage.getItem('token');
|
|
||||||
if (!token) {
|
|
||||||
console.error('Brak tokena. Użytkownik musi być zalogowany.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await axios.get('https://localhost:7039/api/products', {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setProducts(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Błąd podczas pobierania produktów:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchProducts();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleInputChange = (event) => {
|
|
||||||
const { name, value } = event.target;
|
|
||||||
setNewProduct({ ...newProduct, [name]: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAddProduct = async () => {
|
|
||||||
if (!newProduct.name || !newProduct.description || !newProduct.price || !newProduct.type || !newProduct.availability) {
|
|
||||||
setError('Proszę uzupełnić wszystkie pola.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const config = {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
await axios.post('https://localhost:7039/api/Products', newProduct, config);
|
|
||||||
fetchProducts();
|
|
||||||
setIsModalOpen(false);
|
|
||||||
setNewProduct({
|
|
||||||
name: "",
|
|
||||||
availability: "",
|
|
||||||
price: "",
|
|
||||||
type: "",
|
|
||||||
description: ""
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Błąd podczas dodawania:', error);
|
|
||||||
if (error.response && error.response.data) {
|
|
||||||
setError(error.response.data);
|
|
||||||
} else {
|
|
||||||
setError('Wystąpił nieoczekiwany błąd. Spróbuj ponownie później.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEditProduct = (product) => {
|
|
||||||
setEditProduct(product);
|
|
||||||
setIsEditModalOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSaveEditedProduct = async () => {
|
|
||||||
if (!editProduct.name || !editProduct.description || !editProduct.price || !editProduct.type || !editProduct.availability) {
|
|
||||||
setError('Proszę uzupełnić wszystkie pola.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await axios.put(`https://localhost:7039/api/Products/${editProduct.id}`, editProduct);
|
|
||||||
fetchProducts();
|
|
||||||
setIsEditModalOpen(false);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Błąd podczas edycji:', error);
|
|
||||||
if (error.response && error.response.data) {
|
|
||||||
setError(error.response.data);
|
|
||||||
} else {
|
|
||||||
setError('Wystąpił nieoczekiwany błąd. Spróbuj ponownie później.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteProduct = async () => {
|
|
||||||
try {
|
|
||||||
await axios.delete(`https://localhost:7039/api/Products/${deleteProductId}`);
|
|
||||||
fetchProducts();
|
|
||||||
setDeleteProductId(null);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Błąd podczas usuwania produktu:', error);
|
|
||||||
if (error.response && error.response.data) {
|
|
||||||
setError(error.response.data);
|
|
||||||
} else {
|
|
||||||
setError('Wystąpił nieoczekiwany błąd. Spróbuj ponownie później.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const openDeleteConfirmation = (productId) => {
|
|
||||||
setDeleteProductId(productId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeDeleteConfirmation = () => {
|
|
||||||
setDeleteProductId(null);
|
|
||||||
};
|
|
||||||
const formatPrice = (price) => {
|
|
||||||
return parseFloat(price).toFixed(2);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='p-10 ml-11'>
|
<div className="p-10 ml-11">
|
||||||
<div className='flex items-center justify-between'>
|
<Routes>
|
||||||
<div className='h-20 text-5xl ml-1'>
|
<Route
|
||||||
Katalog Produktów
|
path="/"
|
||||||
</div>
|
element={<ListaProduktow onAdd={() => navigate('/produkty/dodaj')} onEdit={(id) => navigate(`/produkty/edytuj/${id}`)} />}
|
||||||
<button onClick={() => setIsModalOpen(true)} className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded flex">
|
|
||||||
<img src={plusIcon} alt="" className="w-8 h-8 mr-2" />Dodaj
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{error && (
|
|
||||||
<div className="absolute top-0 left-0 w-full h-full bg-black bg-opacity-50 flex items-center justify-center">
|
|
||||||
<div className="bg-white p-8 rounded-lg">
|
|
||||||
<h2 className="text-2xl font-bold mb-4">Błąd</h2>
|
|
||||||
<p>{error}</p>
|
|
||||||
<button onClick={() => window.location.reload()} className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">
|
|
||||||
Zamknij
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isEditModalOpen && editProduct && (
|
|
||||||
<div className="absolute top-0 left-0 w-full h-full bg-black bg-opacity-50 flex items-center justify-center z-50">
|
|
||||||
<div className="bg-white p-8 rounded-lg shadow-lg">
|
|
||||||
<h2 className="text-2xl font-bold mb-4">Edytuj produkt</h2>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="name"
|
|
||||||
value={editProduct.name}
|
|
||||||
onChange={(e) => setEditProduct({ ...editProduct, name: e.target.value })}
|
|
||||||
placeholder="Nazwa produktu"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="description"
|
|
||||||
value={editProduct.description}
|
|
||||||
onChange={(e) => setEditProduct({ ...editProduct, description: e.target.value })}
|
|
||||||
placeholder="Opis"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="price"
|
|
||||||
value={editProduct.price}
|
|
||||||
onChange={(e) => setEditProduct({ ...editProduct, price: e.target.value })}
|
|
||||||
placeholder="Cena"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="type"
|
|
||||||
value={editProduct.type}
|
|
||||||
onChange={(e) => setEditProduct({ ...editProduct, type: e.target.value })}
|
|
||||||
placeholder="Typ"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="availability"
|
|
||||||
value={editProduct.availability}
|
|
||||||
onChange={(e) => setEditProduct({ ...editProduct, availability: e.target.value })}
|
|
||||||
placeholder="Dostępność"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
handleSaveEditedProduct();
|
|
||||||
setIsEditModalOpen(false);
|
|
||||||
}}
|
|
||||||
className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
|
|
||||||
>
|
|
||||||
Zapisz zmiany
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setIsEditModalOpen(false)}
|
|
||||||
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
|
|
||||||
>
|
|
||||||
Anuluj
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isModalOpen && (
|
|
||||||
<div className="absolute top-0 left-0 w-full h-full bg-black bg-opacity-50 flex items-center justify-center z-70">
|
|
||||||
<div className="bg-white p-8 rounded-lg shadow-lg">
|
|
||||||
<h2 className="text-2xl font-bold mb-4">Dodaj nowy produkt</h2>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="name"
|
|
||||||
value={newProduct.name}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
placeholder="Nazwa produktu"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="description"
|
|
||||||
value={newProduct.description}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
placeholder="Opis"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="price"
|
|
||||||
value={newProduct.price}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
placeholder="Cena"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="type"
|
|
||||||
value={newProduct.type}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
placeholder="Kategoria"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="availability"
|
|
||||||
value={newProduct.availability}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
placeholder="Dostępność"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
handleAddProduct();
|
|
||||||
setIsModalOpen(false)
|
|
||||||
}}
|
|
||||||
className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
|
|
||||||
>
|
|
||||||
Dodaj produkt
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setIsModalOpen(false)}
|
|
||||||
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
|
|
||||||
>
|
|
||||||
Anuluj
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="w-8/10 mx-auto mt-2">
|
|
||||||
<div className="h-screen overflow-y-auto">
|
|
||||||
<table className="w-full border-collapse border border-gray-300">
|
|
||||||
<thead className="bg-gray-200 top-0 z-10 sticky">
|
|
||||||
<tr>
|
|
||||||
<th className="border border-gray-300 p-2">ID</th>
|
|
||||||
<th className="border border-gray-300 p-2">Produkt</th>
|
|
||||||
<th className="border border-gray-300 p-2">Opis</th>
|
|
||||||
<th className="border border-gray-300 p-2">Cena</th>
|
|
||||||
<th className="border border-gray-300 p-2">Kategoria</th>
|
|
||||||
<th className="border border-gray-300 p-2">Dostępność</th>
|
|
||||||
<th className="border border-gray-300 p-2"></th>
|
|
||||||
<th className="border border-gray-300 p-2"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{products.map(product => (
|
|
||||||
<tr key={product.id}>
|
|
||||||
<td className="border border-gray-300 p-2">{product.id}</td>
|
|
||||||
<td className="border border-gray-300 p-2">{product.name}</td>
|
|
||||||
<td className="border border-gray-300 p-2">{product.description}</td>
|
|
||||||
<td className="border border-gray-300 p-2">{formatPrice(product.price)}</td>
|
|
||||||
<td className="border border-gray-300 p-2">{product.type}</td>
|
|
||||||
<td className="border border-gray-300 p-2">{product.availability}</td>
|
|
||||||
<td className="border border-gray-300 p-2"><button onClick={() => handleEditProduct(product)}
|
|
||||||
className="mr-2 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded flex">
|
|
||||||
<img src={editIcon} alt="" className="w-8 h-8 mr-2" />Edytuj
|
|
||||||
</button></td>
|
|
||||||
<td className="border border-gray-300 p-2">
|
|
||||||
<button onClick={() => openDeleteConfirmation(product.id)}
|
|
||||||
className="mr-2 bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded flex">
|
|
||||||
<img src={koszIcon} alt="" className="w-8 h-8 mr-2" />Usuń
|
|
||||||
</button></td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{deleteProductId && (
|
|
||||||
<ConfirmationModal
|
|
||||||
message="Czy na pewno chcesz usunąć ten produkt?"
|
|
||||||
onCancel={closeDeleteConfirmation}
|
|
||||||
onConfirm={ () => {handleDeleteProduct(); setDeleteProductId(false);}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
<Route path="/dodaj" element={<DodawanieProduktu />} />
|
||||||
|
<Route path="/edytuj/:id" element={<EdycjaProduktu />} />
|
||||||
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
24
firm/src/components/ZarzadzanieTransakcjami.js
Normal file
24
firm/src/components/ZarzadzanieTransakcjami.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Routes, Route, useNavigate } from 'react-router-dom';
|
||||||
|
import ListaTransakcji from './ListaTransakcji';
|
||||||
|
import DodawanieTransakcji from './DodawanieTransakcji';
|
||||||
|
import EdycjaTransakcji from './EdycjaTransakcji';
|
||||||
|
|
||||||
|
const ZarzadzanieTransakcjami = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-10 ml-11">
|
||||||
|
<Routes>
|
||||||
|
<Route
|
||||||
|
path="/"
|
||||||
|
element={<ListaTransakcji onAdd={() => navigate('/transakcje/dodaj')} onEdit={(id) => navigate(`/transakcje/edytuj/${id}`)} />}
|
||||||
|
/>
|
||||||
|
<Route path="/dodaj" element={<DodawanieTransakcji />} />
|
||||||
|
<Route path="/edytuj/:id" element={<EdycjaTransakcji />} />
|
||||||
|
</Routes>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ZarzadzanieTransakcjami;
|
Loading…
Reference in New Issue
Block a user