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 { useNavigate } from 'react-router-dom';
|
||||
|
||||
@ -13,6 +13,15 @@ const DodawanieProduktu = () => {
|
||||
});
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (newProduct.type === '0') {
|
||||
setNewProduct(prevState => ({
|
||||
...prevState,
|
||||
availability: '',
|
||||
}));
|
||||
}
|
||||
}, [newProduct.type]);
|
||||
|
||||
const handleInputChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
setNewProduct({ ...newProduct, [name]: value });
|
||||
|
@ -11,7 +11,7 @@ const EdycjaProduktu = () => {
|
||||
type: '1',
|
||||
availability: '',
|
||||
});
|
||||
const [error, setError] = useState(null);
|
||||
const [errors, setErrors] = useState({});
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
@ -19,7 +19,7 @@ const EdycjaProduktu = () => {
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) {
|
||||
setError('Brak tokena. Użytkownik musi być zalogowany.');
|
||||
setErrors({ general: 'Brak tokena. Użytkownik musi być zalogowany.' });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -28,10 +28,18 @@ const EdycjaProduktu = () => {
|
||||
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) {
|
||||
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 { 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 { name, description, price, type, availability } = product;
|
||||
|
||||
if (!name || !description || !price || type === '') {
|
||||
setError('Proszę uzupełnić wszystkie wymagane pola.');
|
||||
let validationErrors = {};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
const payload = {
|
||||
id,
|
||||
name,
|
||||
@ -59,89 +95,106 @@ const EdycjaProduktu = () => {
|
||||
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.');
|
||||
setErrors({ general: '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);
|
||||
|
||||
await axios.put(`https://localhost:7039/api/Products/${id}`, payload, config);
|
||||
setErrors({});
|
||||
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ę.');
|
||||
setErrors({ general: error.response.data });
|
||||
} 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 (
|
||||
<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' && (
|
||||
{errors.general && <p className="text-red-500 mb-4">{errors.general}</p>}
|
||||
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<div className="relative">
|
||||
<input
|
||||
type="number"
|
||||
name="availability"
|
||||
value={product.availability}
|
||||
type="text"
|
||||
name="name"
|
||||
value={product.name}
|
||||
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"
|
||||
/>
|
||||
{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 className="mt-4">
|
||||
<button
|
||||
onClick={handleSaveChanges}
|
||||
|
@ -1,238 +1,292 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import Select from 'react-select';
|
||||
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 { id } = useParams();
|
||||
const [transaction, setTransaction] = useState({
|
||||
date: '',
|
||||
employeeId: '',
|
||||
paymentType: '',
|
||||
discount: '',
|
||||
description: '',
|
||||
transactionProducts: [{ productID: '', productName: '', quantity: '' }],
|
||||
id: 2,
|
||||
date: "",
|
||||
employeeId: "",
|
||||
transactionProducts: [],
|
||||
paymentType: "",
|
||||
discount: "",
|
||||
description: "",
|
||||
totalPrice: 0,
|
||||
});
|
||||
const [products, setProducts] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [errors, setErrors] = useState({});
|
||||
const navigate = useNavigate();
|
||||
|
||||
const getToken = () => {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) {
|
||||
setError('Brak tokena. Użytkownik musi być zalogowany.');
|
||||
setError("Brak tokena. Użytkownik musi być zalogowany.");
|
||||
return null;
|
||||
}
|
||||
return token;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchTransaction = async () => {
|
||||
const fetchTransactionData = async () => {
|
||||
const token = getToken();
|
||||
if (!token) return;
|
||||
|
||||
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 [transactionRes, productsRes] = await Promise.all([
|
||||
axios.get(`https://localhost:7039/api/transaction/${id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
}),
|
||||
axios.get("https://localhost:7039/api/Products", {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
}),
|
||||
]);
|
||||
|
||||
const updatedTransaction = {
|
||||
...transactionResponse.data,
|
||||
transactionProducts: transactionResponse.data.transactionProducts.map((transactionProduct) => ({
|
||||
productID: transactionProduct.product.id,
|
||||
productName: transactionProduct.product.name,
|
||||
quantity: transactionProduct.quantity,
|
||||
...transactionRes.data,
|
||||
transactionProducts: transactionRes.data.transactionProducts.map((tp) => ({
|
||||
id: tp.id,
|
||||
transactionId: transactionRes.data.id,
|
||||
productID: tp.product.id,
|
||||
productName: tp.product.name,
|
||||
quantity: tp.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) => ({
|
||||
|
||||
const productOptions = productsRes.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);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
setError("Wystąpił błąd podczas ładowania danych.");
|
||||
}
|
||||
};
|
||||
|
||||
fetchTransaction();
|
||||
|
||||
fetchTransactionData();
|
||||
}, [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 { 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 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: '' },
|
||||
],
|
||||
setTransaction((prev) => {
|
||||
const updatedProducts = prev.transactionProducts.map((product, i) =>
|
||||
i === index
|
||||
? { ...product, productID: selectedOption.value, productName: selectedOption.label }
|
||||
: product
|
||||
);
|
||||
return { ...prev, transactionProducts: updatedProducts };
|
||||
});
|
||||
setErrors((prevErrors) => ({ ...prevErrors, [`productID_${index}`]: null }));
|
||||
};
|
||||
|
||||
const handleRemoveProduct = (index) => {
|
||||
const updatedTransactionProducts = [...transaction.transactionProducts];
|
||||
updatedTransactionProducts.splice(index, 1);
|
||||
setTransaction({
|
||||
...transaction,
|
||||
transactionProducts: updatedTransactionProducts,
|
||||
const handleQuantityChange = (index, quantity) => {
|
||||
setTransaction((prev) => {
|
||||
const updatedProducts = prev.transactionProducts.map((product, i) =>
|
||||
i === index ? { ...product, quantity } : product
|
||||
);
|
||||
return { ...prev, transactionProducts: updatedProducts };
|
||||
});
|
||||
setErrors((prevErrors) => ({ ...prevErrors, [`quantity_${index}`]: null }));
|
||||
};
|
||||
|
||||
const handleSaveChanges = async () => {
|
||||
if (!transaction.date || !transaction.employeeId || transaction.transactionProducts.some(product => !product.productName || !product.quantity)) {
|
||||
setError('Proszę uzupełnić wszystkie pola.');
|
||||
const handleRemoveProduct = async (index) => {
|
||||
const token = getToken();
|
||||
if (!token) {
|
||||
console.error("Brak tokena, nie można usunąć produktu.");
|
||||
setError("Użytkownik musi być zalogowany.");
|
||||
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 {
|
||||
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.');
|
||||
await axios.put(
|
||||
`https://localhost:7039/api/transaction/${transaction.id}`,
|
||||
updatedTransaction,
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
);
|
||||
navigate("/transakcje");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
setError(err.response.data);
|
||||
}
|
||||
};
|
||||
|
||||
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>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="mb-4">
|
||||
<input
|
||||
type="datetime-local"
|
||||
name="date"
|
||||
value={transaction.date}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Data"
|
||||
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>}
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<input
|
||||
type="number"
|
||||
name="employeeId"
|
||||
value={transaction.employeeId}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Nr. Pracownika"
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||
/>
|
||||
{errors.employeeId && <span className="text-red-500 text-sm">{errors.employeeId}</span>}
|
||||
</div>
|
||||
{transaction.transactionProducts.map((product, index) => (
|
||||
<div key={index} className="mb-4">
|
||||
<Select
|
||||
value={products.find((p) => p.value === product.productID) || null}
|
||||
onChange={(option) => handleProductChange(index, option)}
|
||||
options={products}
|
||||
className="mb-2"
|
||||
placeholder="Wybierz produkt"
|
||||
/>
|
||||
{errors[`productID_${index}`] && (
|
||||
<span className="text-red-500 text-sm">{errors[`productID_${index}`]}</span>
|
||||
)}
|
||||
<input
|
||||
type="number"
|
||||
value={product.quantity}
|
||||
onChange={(e) => handleQuantityChange(index, e.target.value)}
|
||||
placeholder="Ilość"
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||
/>
|
||||
{errors[`quantity_${index}`] && (
|
||||
<span className="text-red-500 text-sm">{errors[`quantity_${index}`]}</span>
|
||||
)}
|
||||
<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"
|
||||
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 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"
|
||||
/>
|
||||
|
||||
<div className="mb-4">
|
||||
<input
|
||||
type="text"
|
||||
name="paymentType"
|
||||
value={transaction.paymentType}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Sposób płatności"
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||
/>
|
||||
{errors.paymentType && (
|
||||
<span className="text-red-500 text-sm">{errors.paymentType}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<input
|
||||
type="number"
|
||||
name="discount"
|
||||
value={transaction.discount}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Rabat"
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||
/>
|
||||
{errors.discount && <span className="text-red-500 text-sm">{errors.discount}</span>}
|
||||
</div>
|
||||
<button
|
||||
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
|
||||
</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 [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 (
|
||||
<div>
|
||||
<h1>Harmonogram</h1>
|
||||
<div className="container mx-auto px-4 py-6">
|
||||
<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 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.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">{product.type === 0 ? "" : product.availability}</td>
|
||||
<td className="p-2 border">
|
||||
<button
|
||||
onClick={() => handleEditProduct(product.id)}
|
||||
|
@ -16,7 +16,7 @@ const Navbar = ({ setToken }) => {
|
||||
};
|
||||
|
||||
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">
|
||||
<Link to="/" className="text-2xl font-customFont font-bold tracking-wide">FIRMTRACKER</Link>
|
||||
</div>
|
||||
|
@ -9,16 +9,16 @@ import raportyIcon from "../icons/raport.png";
|
||||
|
||||
const Sidebar = ({ userRole }) => {
|
||||
return (
|
||||
<div className="bg-gray-200 h-screen flex justify-center marign-0 w-max">
|
||||
<ul className="">
|
||||
{userRole !== 'User' && (
|
||||
<>
|
||||
<Link to="/panel" className="text-black px-10 py-2 block font-customFont text-center w-max">
|
||||
<li className='flex items-center'>
|
||||
<img src={adminIcon} alt="Obrazek 1" className="w-7 h-7 mr-2" />
|
||||
Panel Administratora
|
||||
</li></Link>
|
||||
</>
|
||||
<div className="bg-gray-200 h-screen flex-grow justify-center w-max sticky top-0 z-0">
|
||||
<ul>
|
||||
{userRole !== 'User' && (
|
||||
<>
|
||||
<Link to="/panel" className="text-black px-10 py-2 block font-customFont text-center w-max">
|
||||
<li className='flex items-center'>
|
||||
<img src={adminIcon} alt="Obrazek 1" className="w-7 h-7 mr-2" />
|
||||
Panel Administratora
|
||||
</li></Link>
|
||||
</>
|
||||
)}
|
||||
<Link to="/produkty" className="text-black px-10 py-2 block font-customFont text-center w-max">
|
||||
<li className='flex items-center'>
|
||||
@ -51,6 +51,7 @@ const Sidebar = ({ userRole }) => {
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
export default Sidebar;
|
||||
|
Loading…
Reference in New Issue
Block a user