poprawa edycji transakcji, dodanie harmonogramu
This commit is contained in:
parent
03bf26a8a1
commit
c4cd38ecac
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
@ -13,6 +13,15 @@ const DodawanieProduktu = () => {
|
|||||||
});
|
});
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (newProduct.type === '0') {
|
||||||
|
setNewProduct(prevState => ({
|
||||||
|
...prevState,
|
||||||
|
availability: '',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, [newProduct.type]);
|
||||||
|
|
||||||
const handleInputChange = (event) => {
|
const handleInputChange = (event) => {
|
||||||
const { name, value } = event.target;
|
const { name, value } = event.target;
|
||||||
setNewProduct({ ...newProduct, [name]: value });
|
setNewProduct({ ...newProduct, [name]: value });
|
||||||
|
@ -11,7 +11,7 @@ const EdycjaProduktu = () => {
|
|||||||
type: '1',
|
type: '1',
|
||||||
availability: '',
|
availability: '',
|
||||||
});
|
});
|
||||||
const [error, setError] = useState(null);
|
const [errors, setErrors] = useState({});
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -19,7 +19,7 @@ const EdycjaProduktu = () => {
|
|||||||
try {
|
try {
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
if (!token) {
|
if (!token) {
|
||||||
setError('Brak tokena. Użytkownik musi być zalogowany.');
|
setErrors({ general: 'Brak tokena. Użytkownik musi być zalogowany.' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,10 +28,18 @@ const EdycjaProduktu = () => {
|
|||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
setProduct(response.data);
|
|
||||||
|
const productData = response.data;
|
||||||
|
setProduct({
|
||||||
|
name: productData.name,
|
||||||
|
description: productData.description,
|
||||||
|
price: productData.price,
|
||||||
|
type: productData.type.toString(),
|
||||||
|
availability: productData.availability || '',
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Błąd podczas pobierania produktu:', error);
|
console.error('Błąd podczas pobierania produktu:', error);
|
||||||
setError('Wystąpił błąd podczas pobierania danych produktu.');
|
setErrors({ general: 'Wystąpił błąd podczas pobierania danych produktu.' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,17 +48,45 @@ const EdycjaProduktu = () => {
|
|||||||
|
|
||||||
const handleInputChange = (event) => {
|
const handleInputChange = (event) => {
|
||||||
const { name, value } = event.target;
|
const { name, value } = event.target;
|
||||||
setProduct({ ...product, [name]: value });
|
|
||||||
|
if (name === 'type') {
|
||||||
|
if (value === '0') {
|
||||||
|
setProduct({
|
||||||
|
...product,
|
||||||
|
[name]: value,
|
||||||
|
availability: '',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setProduct({
|
||||||
|
...product,
|
||||||
|
[name]: value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setProduct({ ...product, [name]: value });
|
||||||
|
}
|
||||||
|
setErrors((prevErrors) => ({
|
||||||
|
...prevErrors,
|
||||||
|
[name]: null,
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSaveChanges = async () => {
|
const handleSaveChanges = async () => {
|
||||||
const { name, description, price, type, availability } = product;
|
const { name, description, price, type, availability } = product;
|
||||||
|
let validationErrors = {};
|
||||||
if (!name || !description || !price || type === '') {
|
|
||||||
setError('Proszę uzupełnić wszystkie wymagane pola.');
|
if (!name) validationErrors.name = 'Nazwa produktu jest wymagana.';
|
||||||
|
if (!description) validationErrors.description = 'Opis produktu jest wymagany.';
|
||||||
|
if (!price) validationErrors.price = 'Cena produktu jest wymagana.';
|
||||||
|
if (type === '') validationErrors.type = 'Typ produktu jest wymagany.';
|
||||||
|
if (price < 0) validationErrors.price = 'Cena nie może być ujemna.';
|
||||||
|
if (type === '1' && !availability) validationErrors.availability = 'Dostępność jest wymagana dla produktu.';
|
||||||
|
|
||||||
|
if (Object.keys(validationErrors).length > 0) {
|
||||||
|
setErrors(validationErrors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
@ -59,89 +95,106 @@ const EdycjaProduktu = () => {
|
|||||||
type: parseInt(type, 10),
|
type: parseInt(type, 10),
|
||||||
...(type === '1' && { availability: availability || 0 }),
|
...(type === '1' && { availability: availability || 0 }),
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
if (!token) {
|
if (!token) {
|
||||||
setError('Brak tokena. Użytkownik musi być zalogowany.');
|
setErrors({ general: 'Brak tokena. Użytkownik musi być zalogowany.' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await axios.put(`https://localhost:7039/api/Products/${id}`, payload, config);
|
await axios.put(`https://localhost:7039/api/Products/${id}`, payload, config);
|
||||||
|
setErrors({});
|
||||||
console.log('Produkt zapisany:', response.data);
|
|
||||||
|
|
||||||
setError(null);
|
|
||||||
navigate('/produkty');
|
navigate('/produkty');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Błąd podczas zapisywania zmian:', error);
|
console.error('Błąd podczas zapisywania zmian:', error);
|
||||||
|
|
||||||
if (error.response && error.response.status === 400) {
|
if (error.response && error.response.status === 400) {
|
||||||
setError('ID produktu nie zgadza się.');
|
setErrors({ general: error.response.data });
|
||||||
} else {
|
} else {
|
||||||
setError('Wystąpił błąd podczas zapisywania zmian. Spróbuj ponownie.');
|
setErrors({ general: 'Wystąpił błąd podczas zapisywania zmian. Spróbuj ponownie.' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-8 bg-gray-100 rounded-lg shadow-md">
|
<div className="p-8 bg-gray-100 rounded-lg shadow-md">
|
||||||
<h2 className="text-2xl font-bold mb-4">Edycja produktu</h2>
|
<h2 className="text-2xl font-bold mb-4">Edycja produktu</h2>
|
||||||
{error && <p className="text-red-500 mb-4">{error}</p>}
|
{errors.general && <p className="text-red-500 mb-4">{errors.general}</p>}
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<input
|
<div className="grid grid-cols-2 gap-8">
|
||||||
type="text"
|
<div className="relative">
|
||||||
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
|
<input
|
||||||
type="number"
|
type="text"
|
||||||
name="availability"
|
name="name"
|
||||||
value={product.availability}
|
value={product.name}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
placeholder="Dostępność (ilość)"
|
placeholder="Nazwa produktu lub usługi"
|
||||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
/>
|
/>
|
||||||
|
{errors.name && <span className="absolute text-red-500 text-sm">{errors.name}</span>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative">
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
{errors.description && <span className="absolute text-red-500 text-sm">{errors.description}</span>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative">
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
{errors.price && <span className="absolute text-red-500 text-sm">{errors.price}</span>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative">
|
||||||
|
<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>
|
||||||
|
{errors.type && <span className="absolute text-red-500 text-sm">{errors.type}</span>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{product.type === '1' && (
|
||||||
|
<div className="relative">
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
{errors.availability && <span className="absolute text-red-500 text-sm">{errors.availability}</span>}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<button
|
<button
|
||||||
onClick={handleSaveChanges}
|
onClick={handleSaveChanges}
|
||||||
|
@ -1,238 +1,292 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from "react";
|
||||||
import axios from 'axios';
|
import axios from "axios";
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from "react-router-dom";
|
||||||
import Select from 'react-select';
|
import Select from "react-select";
|
||||||
|
|
||||||
const EdycjaTransakcji = () => {
|
const EdycjaTransakcji = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const [transaction, setTransaction] = useState({
|
const [transaction, setTransaction] = useState({
|
||||||
date: '',
|
id: 2,
|
||||||
employeeId: '',
|
date: "",
|
||||||
paymentType: '',
|
employeeId: "",
|
||||||
discount: '',
|
transactionProducts: [],
|
||||||
description: '',
|
paymentType: "",
|
||||||
transactionProducts: [{ productID: '', productName: '', quantity: '' }],
|
discount: "",
|
||||||
|
description: "",
|
||||||
|
totalPrice: 0,
|
||||||
});
|
});
|
||||||
const [products, setProducts] = useState([]);
|
const [products, setProducts] = useState([]);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
|
const [errors, setErrors] = useState({});
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const getToken = () => {
|
const getToken = () => {
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem("token");
|
||||||
if (!token) {
|
if (!token) {
|
||||||
setError('Brak tokena. Użytkownik musi być zalogowany.');
|
setError("Brak tokena. Użytkownik musi być zalogowany.");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return token;
|
return token;
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchTransaction = async () => {
|
const fetchTransactionData = async () => {
|
||||||
|
const token = getToken();
|
||||||
|
if (!token) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const token = getToken();
|
const [transactionRes, productsRes] = await Promise.all([
|
||||||
if (!token) return;
|
axios.get(`https://localhost:7039/api/transaction/${id}`, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
const transactionResponse = await axios.get(`https://localhost:7039/api/transaction/${id}`, {
|
}),
|
||||||
headers: {
|
axios.get("https://localhost:7039/api/Products", {
|
||||||
Authorization: `Bearer ${token}`,
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
},
|
}),
|
||||||
});
|
]);
|
||||||
console.log('Dane transakcji:', transactionResponse.data);
|
|
||||||
|
|
||||||
const updatedTransaction = {
|
const updatedTransaction = {
|
||||||
...transactionResponse.data,
|
...transactionRes.data,
|
||||||
transactionProducts: transactionResponse.data.transactionProducts.map((transactionProduct) => ({
|
transactionProducts: transactionRes.data.transactionProducts.map((tp) => ({
|
||||||
productID: transactionProduct.product.id,
|
id: tp.id,
|
||||||
productName: transactionProduct.product.name,
|
transactionId: transactionRes.data.id,
|
||||||
quantity: transactionProduct.quantity,
|
productID: tp.product.id,
|
||||||
|
productName: tp.product.name,
|
||||||
|
quantity: tp.quantity,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
setTransaction(updatedTransaction);
|
setTransaction(updatedTransaction);
|
||||||
|
|
||||||
const productResponse = await axios.get('https://localhost:7039/api/Products', {
|
const productOptions = productsRes.data.map((product) => ({
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
console.log('Produkty:', productResponse.data);
|
|
||||||
|
|
||||||
const productOptions = productResponse.data.map((product) => ({
|
|
||||||
value: product.id,
|
value: product.id,
|
||||||
label: product.name,
|
label: product.name,
|
||||||
}));
|
}));
|
||||||
setProducts(productOptions);
|
setProducts(productOptions);
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
console.error('Błąd podczas pobierania transakcji lub produktów:', error);
|
console.error(err);
|
||||||
setError('Wystąpił błąd podczas ładowania danych.');
|
setError("Wystąpił błąd podczas ładowania danych.");
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchTransaction();
|
fetchTransactionData();
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
|
const validateForm = () => {
|
||||||
|
const validationErrors = {};
|
||||||
|
if (!transaction.date) validationErrors.date = "Data transakcji jest wymagana.";
|
||||||
|
if (!transaction.employeeId || transaction.employeeId <= 0) validationErrors.employeeId = "Numer pracownika jest błędny.";
|
||||||
|
if (transaction.transactionProducts.length === 0)
|
||||||
|
validationErrors.transactionProducts = "Transakcja musi zawierać co najmniej jeden produkt.";
|
||||||
|
else {
|
||||||
|
transaction.transactionProducts.forEach((product, index) => {
|
||||||
|
if (!product.productID) validationErrors[`productID_${index}`] = "Produkt jest wymagany.";
|
||||||
|
if (!product.quantity || product.quantity <= 0)
|
||||||
|
validationErrors[`quantity_${index}`] = "Ilość musi być większa niż 0.";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!transaction.paymentType) validationErrors.paymentType = "Sposób płatności jest wymagany.";
|
||||||
|
if (transaction.discount < 0) validationErrors.discount = "Rabat nie może być ujemny.";
|
||||||
|
|
||||||
|
setErrors(validationErrors);
|
||||||
|
return Object.keys(validationErrors).length === 0;
|
||||||
|
};
|
||||||
|
|
||||||
const handleInputChange = (event) => {
|
const handleInputChange = (event) => {
|
||||||
const { name, value } = event.target;
|
const { name, value } = event.target;
|
||||||
setTransaction({ ...transaction, [name]: value });
|
setTransaction((prev) => ({ ...prev, [name]: value }));
|
||||||
|
setErrors((prevErrors) => ({ ...prevErrors, [name]: null }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddProduct = () => {
|
||||||
|
setTransaction((prev) => ({
|
||||||
|
...prev,
|
||||||
|
transactionProducts: [
|
||||||
|
...prev.transactionProducts,
|
||||||
|
{ id: null, transactionId: prev.id, productID: 0, productName: "", quantity: "" },
|
||||||
|
],
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleProductChange = (index, selectedOption) => {
|
const handleProductChange = (index, selectedOption) => {
|
||||||
const updatedTransactionProducts = [...transaction.transactionProducts];
|
setTransaction((prev) => {
|
||||||
updatedTransactionProducts[index].productID = selectedOption.value;
|
const updatedProducts = prev.transactionProducts.map((product, i) =>
|
||||||
updatedTransactionProducts[index].productName = selectedOption.label;
|
i === index
|
||||||
setTransaction({
|
? { ...product, productID: selectedOption.value, productName: selectedOption.label }
|
||||||
...transaction,
|
: product
|
||||||
transactionProducts: updatedTransactionProducts,
|
);
|
||||||
});
|
return { ...prev, transactionProducts: updatedProducts };
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const handleAddProduct = () => {
|
|
||||||
setTransaction({
|
|
||||||
...transaction,
|
|
||||||
transactionProducts: [
|
|
||||||
...transaction.transactionProducts,
|
|
||||||
{ productID: 0, productName: '', quantity: '' },
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
setErrors((prevErrors) => ({ ...prevErrors, [`productID_${index}`]: null }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemoveProduct = (index) => {
|
const handleQuantityChange = (index, quantity) => {
|
||||||
const updatedTransactionProducts = [...transaction.transactionProducts];
|
setTransaction((prev) => {
|
||||||
updatedTransactionProducts.splice(index, 1);
|
const updatedProducts = prev.transactionProducts.map((product, i) =>
|
||||||
setTransaction({
|
i === index ? { ...product, quantity } : product
|
||||||
...transaction,
|
);
|
||||||
transactionProducts: updatedTransactionProducts,
|
return { ...prev, transactionProducts: updatedProducts };
|
||||||
});
|
});
|
||||||
|
setErrors((prevErrors) => ({ ...prevErrors, [`quantity_${index}`]: null }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSaveChanges = async () => {
|
const handleRemoveProduct = async (index) => {
|
||||||
if (!transaction.date || !transaction.employeeId || transaction.transactionProducts.some(product => !product.productName || !product.quantity)) {
|
const token = getToken();
|
||||||
setError('Proszę uzupełnić wszystkie pola.');
|
if (!token) {
|
||||||
|
console.error("Brak tokena, nie można usunąć produktu.");
|
||||||
|
setError("Użytkownik musi być zalogowany.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const productToRemove = transaction.transactionProducts[index];
|
||||||
|
console.log(productToRemove);
|
||||||
|
|
||||||
|
if (!productToRemove || !productToRemove.id) {
|
||||||
|
console.error("Nie znaleziono ID transakcyjnego produktu. Usuwanie lokalne.");
|
||||||
|
setTransaction((prev) => ({
|
||||||
|
...prev,
|
||||||
|
transactionProducts: prev.transactionProducts.filter((_, i) => i !== index),
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.delete(
|
||||||
|
`https://localhost:7039/api/Transaction/${transaction.id}/product/${productToRemove.productID}`,
|
||||||
|
{ headers: { Authorization: `Bearer ${token}` } }
|
||||||
|
);
|
||||||
|
console.log(`Produkt o ID transakcji ${productToRemove.id} został usunięty.`);
|
||||||
|
|
||||||
|
setTransaction((prev) => ({
|
||||||
|
...prev,
|
||||||
|
transactionProducts: prev.transactionProducts.filter((_, i) => i !== index),
|
||||||
|
}));
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Błąd podczas usuwania produktu:", err.response?.data || err.message);
|
||||||
|
setError(err.response?.data?.message || "Nie udało się usunąć produktu. Spróbuj ponownie.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const handleSaveChanges = async () => {
|
||||||
|
if (!validateForm()) return;
|
||||||
|
|
||||||
|
const token = getToken();
|
||||||
|
if (!token) return;
|
||||||
|
|
||||||
|
const updatedTransaction = {
|
||||||
|
...transaction,
|
||||||
|
transactionProducts: transaction.transactionProducts.map((tp) => ({
|
||||||
|
id: tp.id || 0,
|
||||||
|
transactionId: transaction.id,
|
||||||
|
productID: tp.productID,
|
||||||
|
productName: tp.productName,
|
||||||
|
quantity: Number(tp.quantity),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const token = getToken();
|
await axios.put(
|
||||||
if (!token) {
|
`https://localhost:7039/api/transaction/${transaction.id}`,
|
||||||
setError('Brak tokena. Użytkownik musi być zalogowany.');
|
updatedTransaction,
|
||||||
return;
|
{ headers: { Authorization: `Bearer ${token}` } }
|
||||||
}
|
);
|
||||||
|
navigate("/transakcje");
|
||||||
const response = await axios.put(`https://localhost:7039/api/transaction/${id}`, transaction, {
|
} catch (err) {
|
||||||
headers: {
|
console.error(err);
|
||||||
Authorization: `Bearer ${token}`,
|
setError(err.response.data);
|
||||||
},
|
|
||||||
});
|
|
||||||
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 (
|
return (
|
||||||
<div className="bg-white p-8 rounded-lg">
|
<div className="bg-white p-8 rounded-lg">
|
||||||
<h2 className="text-2xl font-bold mb-4">Edycja Transakcji</h2>
|
<h2 className="text-2xl font-bold mb-4">Edycja Transakcji</h2>
|
||||||
|
|
||||||
{error && <p className="text-red-500 mb-4">{error}</p>}
|
{error && <p className="text-red-500 mb-4">{error}</p>}
|
||||||
|
<div className="mb-4">
|
||||||
<input
|
<input
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
name="date"
|
name="date"
|
||||||
value={transaction.date}
|
value={transaction.date}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
placeholder="Data"
|
placeholder="Data"
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
/>
|
/>
|
||||||
|
{errors.date && <span className="text-red-500 text-sm">{errors.date}</span>}
|
||||||
<input
|
</div>
|
||||||
type="number"
|
<div className="mb-4">
|
||||||
name="employeeId"
|
<input
|
||||||
value={transaction.employeeId}
|
type="number"
|
||||||
onChange={handleInputChange}
|
name="employeeId"
|
||||||
placeholder="Nr. Pracownika"
|
value={transaction.employeeId}
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
onChange={handleInputChange}
|
||||||
/>
|
placeholder="Nr. Pracownika"
|
||||||
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
{isLoading ? (
|
/>
|
||||||
<div className="text-center">Ładowanie produktów...</div>
|
{errors.employeeId && <span className="text-red-500 text-sm">{errors.employeeId}</span>}
|
||||||
) : (
|
</div>
|
||||||
<>
|
{transaction.transactionProducts.map((product, index) => (
|
||||||
{transaction.transactionProducts.map((product, index) => (
|
<div key={index} className="mb-4">
|
||||||
<div key={index} className="mb-4">
|
<Select
|
||||||
<Select
|
value={products.find((p) => p.value === product.productID) || null}
|
||||||
name={`productName-${index}`}
|
onChange={(option) => handleProductChange(index, option)}
|
||||||
value={products.find(option => option.value === product.productID) || null}
|
options={products}
|
||||||
onChange={(selectedOption) => handleProductChange(index, selectedOption)}
|
className="mb-2"
|
||||||
options={products}
|
placeholder="Wybierz produkt"
|
||||||
className="block w-full mb-2"
|
/>
|
||||||
placeholder="Wybierz produkt..."
|
{errors[`productID_${index}`] && (
|
||||||
/>
|
<span className="text-red-500 text-sm">{errors[`productID_${index}`]}</span>
|
||||||
<input
|
)}
|
||||||
type="number"
|
<input
|
||||||
name={`quantity-${index}`}
|
type="number"
|
||||||
value={product.quantity}
|
value={product.quantity}
|
||||||
onChange={(e) => {
|
onChange={(e) => handleQuantityChange(index, e.target.value)}
|
||||||
const updatedTransactionProducts = [...transaction.transactionProducts];
|
placeholder="Ilość"
|
||||||
updatedTransactionProducts[index].quantity = e.target.value;
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
setTransaction({ ...transaction, transactionProducts: updatedTransactionProducts });
|
/>
|
||||||
}}
|
{errors[`quantity_${index}`] && (
|
||||||
placeholder="Ilość"
|
<span className="text-red-500 text-sm">{errors[`quantity_${index}`]}</span>
|
||||||
className="block w-full mb-2 px-4 py-2 border border-gray-300 rounded-lg"
|
)}
|
||||||
/>
|
<button
|
||||||
<button
|
onClick={() => handleRemoveProduct(index)}
|
||||||
onClick={() => handleRemoveProduct(index)}
|
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
|
||||||
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
|
>
|
||||||
>
|
Usuń
|
||||||
Usuń
|
</button>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={handleAddProduct}
|
onClick={handleAddProduct}
|
||||||
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 my-3 rounded"
|
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
|
||||||
>
|
>
|
||||||
Dodaj produkt
|
Dodaj produkt
|
||||||
</button>
|
</button>
|
||||||
|
<div className="mb-4">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="paymentType"
|
name="paymentType"
|
||||||
value={transaction.paymentType}
|
value={transaction.paymentType}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
placeholder="Sposób płatności"
|
placeholder="Sposób płatności"
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
/>
|
/>
|
||||||
|
{errors.paymentType && (
|
||||||
<input
|
<span className="text-red-500 text-sm">{errors.paymentType}</span>
|
||||||
type="number"
|
)}
|
||||||
name="discount"
|
</div>
|
||||||
value={transaction.discount}
|
<div className="mb-4">
|
||||||
onChange={handleInputChange}
|
<input
|
||||||
placeholder="Rabat"
|
type="number"
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
name="discount"
|
||||||
/>
|
value={transaction.discount}
|
||||||
|
onChange={handleInputChange}
|
||||||
<input
|
placeholder="Rabat"
|
||||||
type="text"
|
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||||
name="description"
|
/>
|
||||||
value={transaction.description}
|
{errors.discount && <span className="text-red-500 text-sm">{errors.discount}</span>}
|
||||||
onChange={handleInputChange}
|
</div>
|
||||||
placeholder="Opis"
|
|
||||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={handleSaveChanges}
|
onClick={handleSaveChanges}
|
||||||
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 my-3 rounded"
|
className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
|
||||||
>
|
>
|
||||||
Zapisz zmiany
|
Zapisz zmiany
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,10 +1,177 @@
|
|||||||
import React from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { jwtDecode } from 'jwt-decode';
|
||||||
|
|
||||||
const Harmonogram = () => {
|
const Harmonogram = () => {
|
||||||
|
const [workdays, setWorkdays] = useState([]);
|
||||||
|
const [email, setEmail] = useState(null);
|
||||||
|
const [currentDate, setCurrentDate] = useState(new Date());
|
||||||
|
const [displayDate, setDisplayDate] = useState(new Date());
|
||||||
|
const [daysInMonth, setDaysInMonth] = useState([]);
|
||||||
|
const [manualDateChange, setManualDateChange] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
if (token) {
|
||||||
|
const decodedToken = jwtDecode(token);
|
||||||
|
setEmail(decodedToken.email);
|
||||||
|
fetchWorkdays(decodedToken.email);
|
||||||
|
}
|
||||||
|
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
if (!manualDateChange) {
|
||||||
|
setCurrentDate(new Date());
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [manualDateChange]);
|
||||||
|
|
||||||
|
const fetchWorkdays = async (email) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`https://localhost:7039/api/Workday/user/${email}/workdays`, {
|
||||||
|
headers: { Authorization: `Bearer ${localStorage.getItem('token')}` },
|
||||||
|
});
|
||||||
|
setWorkdays(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Błąd podczas pobierania dni roboczych:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStartWorkday = async (date) => {
|
||||||
|
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
if (!token) {
|
||||||
|
alert('Brak tokena. Użytkownik musi być zalogowany.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.post(
|
||||||
|
'https://localhost:7039/api/workday/start',
|
||||||
|
{ date },
|
||||||
|
{
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
alert('Dzień roboczy rozpoczęty!');
|
||||||
|
fetchWorkdays(email);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isWorkday = (date) => {
|
||||||
|
return workdays.includes(date);
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatTime = (date) => {
|
||||||
|
const hours = date.getHours().toString().padStart(2, '0');
|
||||||
|
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||||
|
const seconds = date.getSeconds().toString().padStart(2, '0');
|
||||||
|
return `${hours}:${minutes}:${seconds}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatDayOfWeek = (date) => {
|
||||||
|
const daysOfWeek = ['Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota', 'Niedziela'];
|
||||||
|
return daysOfWeek[date.getDay() === 0 ? 6 : date.getDay() - 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatDate = (date) => {
|
||||||
|
const day = date.getDate().toString().padStart(2, '0');
|
||||||
|
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||||
|
const year = date.getFullYear();
|
||||||
|
return `${day}-${month}-${year}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateDaysInMonth = () => {
|
||||||
|
const firstDayOfMonth = new Date(displayDate.getFullYear(), displayDate.getMonth(), 1);
|
||||||
|
const lastDayOfMonth = new Date(displayDate.getFullYear(), displayDate.getMonth() + 1, 0);
|
||||||
|
|
||||||
|
const firstDayWeekday = (firstDayOfMonth.getDay() === 0 ? 6 : firstDayOfMonth.getDay() - 1);
|
||||||
|
const numberOfDaysInMonth = lastDayOfMonth.getDate();
|
||||||
|
|
||||||
|
const days = [];
|
||||||
|
for (let i = 0; i < firstDayWeekday; i++) {
|
||||||
|
days.push(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 1; i <= numberOfDaysInMonth; i++) {
|
||||||
|
days.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
setDaysInMonth(days);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
generateDaysInMonth();
|
||||||
|
}, [displayDate]);
|
||||||
|
|
||||||
|
const changeMonth = (direction) => {
|
||||||
|
setManualDateChange(true);
|
||||||
|
const newDate = new Date(displayDate);
|
||||||
|
if (direction === 'previous') {
|
||||||
|
newDate.setMonth(displayDate.getMonth() - 1);
|
||||||
|
} else if (direction === 'next') {
|
||||||
|
newDate.setMonth(displayDate.getMonth() + 1);
|
||||||
|
}
|
||||||
|
setDisplayDate(newDate);
|
||||||
|
setTimeout(() => setManualDateChange(false), 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatMonth = (monthIndex) => {
|
||||||
|
const months = [
|
||||||
|
'Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec',
|
||||||
|
'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień',
|
||||||
|
];
|
||||||
|
return months[monthIndex];
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="container mx-auto px-4 py-6">
|
||||||
<h1>Harmonogram</h1>
|
<div className="flex justify-between items-center mb-6">
|
||||||
|
<div className="text-lg font-semibold">
|
||||||
|
<p>{formatDate(currentDate)} - {formatDayOfWeek(currentDate)}</p>
|
||||||
|
<p>{formatTime(currentDate)}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<button
|
||||||
|
className="bg-blue-500 text-white py-2 px-4 rounded"
|
||||||
|
onClick={() => changeMonth('previous')}
|
||||||
|
>
|
||||||
|
Poprzedni miesiąc
|
||||||
|
</button>
|
||||||
|
<h2 className="text-2xl font-bold">{formatMonth(displayDate.getMonth())} {displayDate.getFullYear()}</h2>
|
||||||
|
<button
|
||||||
|
className="bg-blue-500 text-white py-2 px-4 rounded"
|
||||||
|
onClick={() => changeMonth('next')}
|
||||||
|
>
|
||||||
|
Następny miesiąc
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-7 gap-4">
|
||||||
|
{['P', 'W', 'Ś', 'C', 'P', 'S', 'N'].map((day, index) => (
|
||||||
|
<div key={index} className="text-center font-semibold">{day}</div>
|
||||||
|
))}
|
||||||
|
{daysInMonth.map((day, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`text-center py-5 rounded-lg ${day ? 'cursor-pointer' : 'text-transparent'}
|
||||||
|
${day && 'bg-gray-200 hover:bg-gray-300 transition duration-150 ease-in-out'}
|
||||||
|
${isWorkday(formatDate(new Date(displayDate.getFullYear(), displayDate.getMonth(), day))) && 'bg-green-200'}`}
|
||||||
|
style={{
|
||||||
|
boxShadow: day ? '0 4px 6px rgba(0, 0, 0, 0.1)' : 'none',
|
||||||
|
}}
|
||||||
|
onClick={() => day && handleStartWorkday(new Date(displayDate.getFullYear(), displayDate.getMonth(), day))}
|
||||||
|
>
|
||||||
|
{day}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ const ListaProduktow = ({ onAdd }) => {
|
|||||||
<td className="p-2 border">{product.name}</td>
|
<td className="p-2 border">{product.name}</td>
|
||||||
<td className="p-2 border">{product.description}</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">{parseFloat(product.price).toFixed(2)}</td>
|
||||||
<td className="p-2 border">{product.availability}</td>
|
<td className="p-2 border">{product.type === 0 ? "" : product.availability}</td>
|
||||||
<td className="p-2 border">
|
<td className="p-2 border">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleEditProduct(product.id)}
|
onClick={() => handleEditProduct(product.id)}
|
||||||
|
@ -16,7 +16,7 @@ const Navbar = ({ setToken }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between bg-gray-300 p-7 h-16">
|
<div className="flex items-center justify-between bg-gray-300 p-7 h-16 top-0">
|
||||||
<div className="flex items-center flex-shrink-0 text-black mr-6">
|
<div className="flex items-center flex-shrink-0 text-black mr-6">
|
||||||
<Link to="/" className="text-2xl font-customFont font-bold tracking-wide">FIRMTRACKER</Link>
|
<Link to="/" className="text-2xl font-customFont font-bold tracking-wide">FIRMTRACKER</Link>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,16 +9,16 @@ import raportyIcon from "../icons/raport.png";
|
|||||||
|
|
||||||
const Sidebar = ({ userRole }) => {
|
const Sidebar = ({ userRole }) => {
|
||||||
return (
|
return (
|
||||||
<div className="bg-gray-200 h-screen flex justify-center marign-0 w-max">
|
<div className="bg-gray-200 h-screen flex-grow justify-center w-max sticky top-0 z-0">
|
||||||
<ul className="">
|
<ul>
|
||||||
{userRole !== 'User' && (
|
{userRole !== 'User' && (
|
||||||
<>
|
<>
|
||||||
<Link to="/panel" className="text-black px-10 py-2 block font-customFont text-center w-max">
|
<Link to="/panel" className="text-black px-10 py-2 block font-customFont text-center w-max">
|
||||||
<li className='flex items-center'>
|
<li className='flex items-center'>
|
||||||
<img src={adminIcon} alt="Obrazek 1" className="w-7 h-7 mr-2" />
|
<img src={adminIcon} alt="Obrazek 1" className="w-7 h-7 mr-2" />
|
||||||
Panel Administratora
|
Panel Administratora
|
||||||
</li></Link>
|
</li></Link>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Link to="/produkty" className="text-black px-10 py-2 block font-customFont text-center w-max">
|
<Link to="/produkty" className="text-black px-10 py-2 block font-customFont text-center w-max">
|
||||||
<li className='flex items-center'>
|
<li className='flex items-center'>
|
||||||
@ -51,6 +51,7 @@ const Sidebar = ({ userRole }) => {
|
|||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Sidebar;
|
export default Sidebar;
|
||||||
|
Loading…
Reference in New Issue
Block a user