skończone harmonogramy oraz poprawy wizualne
This commit is contained in:
parent
4659ef4b34
commit
b327d3deda
@ -1,22 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const ConfirmationModal = ({ message, onCancel, onConfirm }) => {
|
||||
return (
|
||||
<div className="fixed top-0 left-0 w-full h-full 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">Potwierdź usunięcie</h2>
|
||||
<p>{message}</p>
|
||||
<div className="mt-4 flex justify-end">
|
||||
<button onClick={onConfirm} className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded mr-4">
|
||||
Usuń
|
||||
</button>
|
||||
<button onClick={onCancel} className="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded">
|
||||
Anuluj
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfirmationModal;
|
@ -27,6 +27,10 @@ const DodawanieProduktu = () => {
|
||||
setNewProduct({ ...newProduct, [name]: value });
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
navigate('/produkty');
|
||||
}
|
||||
|
||||
const handleAddProduct = async () => {
|
||||
const { name, description, price, type, availability } = newProduct;
|
||||
|
||||
@ -75,65 +79,73 @@ const DodawanieProduktu = () => {
|
||||
};
|
||||
|
||||
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' && (
|
||||
<div className="min-h-screen flex justify-center pt-14">
|
||||
<div className="p-8 bg-white rounded-lg shadow-lg border border-gray-200 w-full max-w-4xl h-max">
|
||||
<h2 className="text-2xl font-semibold text-gray-900 mb-8">Dodaj nowy produkt lub usługę</h2>
|
||||
{error && <p className="text-red-500 mb-4">{error}</p>}
|
||||
<div className="grid grid-cols-1 md: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 focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||
/>
|
||||
<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 focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
name="availability"
|
||||
value={newProduct.availability}
|
||||
step="0.01"
|
||||
name="price"
|
||||
value={newProduct.price}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Dostępność (ilość)"
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||
placeholder="Cena"
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||
/>
|
||||
)}
|
||||
</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>
|
||||
<select
|
||||
name="type"
|
||||
value={newProduct.type}
|
||||
onChange={handleInputChange}
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||
>
|
||||
<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 focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-6 flex justify-between">
|
||||
<button
|
||||
onClick={handleCancel}
|
||||
className="bg-gradient-to-r from-red-500 to-red-700 text-white py-2 px-4 rounded-lg hover:from-red-600 hover:to-red-800 transition"
|
||||
>Anuluj</button>
|
||||
<button
|
||||
onClick={handleAddProduct}
|
||||
className="bg-gradient-to-r from-green-500 to-green-700 text-white py-2 px-4 rounded-lg hover:from-green-600 hover:to-green-800 transition"
|
||||
>
|
||||
Dodaj
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
};
|
||||
|
||||
export default DodawanieProduktu;
|
||||
|
@ -63,6 +63,9 @@ const DodawanieTransakcji = () => {
|
||||
const { name, value } = event.target;
|
||||
setNewTransaction({ ...newTransaction, [name]: value });
|
||||
};
|
||||
const handleCancel = () => {
|
||||
navigate('/transakcje');
|
||||
}
|
||||
|
||||
const handleProductChange = (index, selectedOption) => {
|
||||
const updatedTransactionProducts = [...newTransaction.transactionProducts];
|
||||
@ -153,29 +156,29 @@ const DodawanieTransakcji = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white p-8 rounded-lg">
|
||||
<h2 className="text-2xl font-bold mb-4">Dodaj nową transakcję</h2>
|
||||
|
||||
<div className="bg-white p-8 rounded-lg shadow-lg max-w-4xl mx-auto mt-6">
|
||||
<h2 className="text-2xl font-bold mb-6 text-gray-800">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"
|
||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
|
||||
|
||||
<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"
|
||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
|
||||
|
||||
{isLoading ? (
|
||||
<div className="text-center">Ładowanie produktów...</div>
|
||||
) : (
|
||||
@ -200,11 +203,11 @@ const DodawanieTransakcji = () => {
|
||||
setNewTransaction({ ...newTransaction, transactionProducts: updatedTransactionProducts });
|
||||
}}
|
||||
placeholder="Ilość"
|
||||
className="block w-full mb-2 px-4 py-2 border border-gray-300 rounded-lg"
|
||||
className="block w-full mb-2 px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
<button
|
||||
onClick={() => handleRemoveProduct(index)}
|
||||
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
|
||||
className="bg-gradient-to-r from-red-500 to-red-700 text-white py-2 px-4 rounded-lg hover:from-red-600 hover:to-red-800 transition"
|
||||
>
|
||||
Usuń
|
||||
</button>
|
||||
@ -212,49 +215,58 @@ const DodawanieTransakcji = () => {
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
<button
|
||||
onClick={handleAddProduct}
|
||||
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 my-3 rounded"
|
||||
className="bg-gradient-to-r from-blue-500 to-blue-700 text-white py-2 px-4 rounded-lg hover:from-blue-600 hover:to-blue-800 transition mb-3"
|
||||
>
|
||||
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"
|
||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
|
||||
|
||||
<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"
|
||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
|
||||
|
||||
<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"
|
||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
|
||||
<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 className="mt-6 flex justify-between">
|
||||
<button
|
||||
onClick={handleAddTransaction}
|
||||
className="bg-gradient-to-r from-blue-500 to-blue-700 text-white font-bold py-2 px-4 rounded-lg shadow-md hover:from-blue-600 hover:to-blue-800 transition"
|
||||
>
|
||||
Dodaj transakcję
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={handleCancel}
|
||||
className="bg-gradient-to-r from-red-500 to-red-700 text-white py-2 px-4 rounded-lg hover:from-red-600 hover:to-red-800 transition"
|
||||
>
|
||||
Anuluj
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
export default DodawanieTransakcji;
|
||||
|
@ -46,6 +46,10 @@ const EdycjaProduktu = () => {
|
||||
fetchProduct();
|
||||
}, [id]);
|
||||
|
||||
const handleCancel = () => {
|
||||
navigate('/produkty');
|
||||
}
|
||||
|
||||
const handleInputChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
|
||||
@ -125,86 +129,92 @@ const EdycjaProduktu = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-8 bg-gray-100 rounded-lg shadow-md">
|
||||
<h2 className="text-2xl font-bold mb-4">Edycja produktu</h2>
|
||||
{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="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"
|
||||
/>
|
||||
{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="min-h-screen flex justify-center pt-10">
|
||||
<div className="p-8 bg-white rounded-lg shadow-lg border border-gray-200 w-full max-w-4xl h-max">
|
||||
<h2 className="text-2xl font-semibold text-gray-900 mb-4">Edycja produktu</h2>
|
||||
{errors.general && <p className="text-red-500 mb-4">{errors.general}</p>}
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="relative">
|
||||
<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 focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||
/>
|
||||
{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 focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||
/>
|
||||
{errors.description && <span className="absolute text-red-500 text-sm">{errors.description}</span>}
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<input
|
||||
type="number"
|
||||
name="availability"
|
||||
value={product.availability}
|
||||
step="0.01"
|
||||
name="price"
|
||||
value={product.price}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Dostępność (ilość)"
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg"
|
||||
placeholder="Cena"
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||
/>
|
||||
{errors.availability && <span className="absolute text-red-500 text-sm">{errors.availability}</span>}
|
||||
{errors.price && <span className="absolute text-red-500 text-sm">{errors.price}</span>}
|
||||
</div>
|
||||
)}
|
||||
</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 className="relative">
|
||||
<select
|
||||
name="type"
|
||||
value={product.type}
|
||||
onChange={handleInputChange}
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||
>
|
||||
<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 focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||
/>
|
||||
{errors.availability && <span className="absolute text-red-500 text-sm">{errors.availability}</span>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-6 flex justify-between">
|
||||
<button
|
||||
onClick={handleCancel}
|
||||
className="bg-gradient-to-r from-red-500 to-red-700 text-white py-2 px-4 rounded-lg hover:from-red-600 hover:to-red-800 transition"
|
||||
>Anuluj</button>
|
||||
<button
|
||||
onClick={handleSaveChanges}
|
||||
className="bg-gradient-to-r from-green-500 to-green-700 text-white py-2 px-4 rounded-lg hover:from-green-600 hover:to-green-800 transition"
|
||||
>
|
||||
Zapisz zmiany
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
export default EdycjaProduktu;
|
||||
|
@ -90,6 +90,10 @@ const EdycjaTransakcji = () => {
|
||||
return Object.keys(validationErrors).length === 0;
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
navigate('/transkacje');
|
||||
}
|
||||
|
||||
const handleInputChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
setTransaction((prev) => ({ ...prev, [name]: value }));
|
||||
@ -199,9 +203,10 @@ const EdycjaTransakcji = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white p-8 rounded-lg">
|
||||
<h2 className="text-2xl font-bold mb-4">Edycja Transakcji</h2>
|
||||
<div className="bg-white p-8 rounded-lg shadow-lg max-w-4xl mx-auto mt-6">
|
||||
<h2 className="text-2xl font-bold mb-6 text-gray-800">Edycja Transakcji</h2>
|
||||
{error && <p className="text-red-500 mb-4">{error}</p>}
|
||||
|
||||
<div className="mb-4">
|
||||
<input
|
||||
type="datetime-local"
|
||||
@ -209,10 +214,11 @@ const EdycjaTransakcji = () => {
|
||||
value={transaction.date}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Data"
|
||||
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 shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
{errors.date && <span className="text-red-500 text-sm">{errors.date}</span>}
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<input
|
||||
type="number"
|
||||
@ -220,10 +226,11 @@ const EdycjaTransakcji = () => {
|
||||
value={transaction.employeeId}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Nr. Pracownika"
|
||||
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 shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
{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
|
||||
@ -241,25 +248,27 @@ const EdycjaTransakcji = () => {
|
||||
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"
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
{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"
|
||||
className="bg-gradient-to-r from-red-500 to-red-700 text-white py-2 px-4 rounded-lg hover:from-red-600 hover:to-red-800 transition mt-3"
|
||||
>
|
||||
Usuń
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<button
|
||||
onClick={handleAddProduct}
|
||||
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
|
||||
className="bg-gradient-to-r from-blue-500 to-blue-700 text-white font-bold py-2 px-4 mb-3 rounded-lg shadow-md hover:from-blue-600 hover:to-blue-800 transition"
|
||||
>
|
||||
Dodaj produkt
|
||||
</button>
|
||||
|
||||
<div className="mb-4">
|
||||
<input
|
||||
type="text"
|
||||
@ -267,12 +276,11 @@ const EdycjaTransakcji = () => {
|
||||
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"
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
{errors.paymentType && (
|
||||
<span className="text-red-500 text-sm">{errors.paymentType}</span>
|
||||
)}
|
||||
{errors.paymentType && <span className="text-red-500 text-sm">{errors.paymentType}</span>}
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<input
|
||||
type="number"
|
||||
@ -280,18 +288,27 @@ const EdycjaTransakcji = () => {
|
||||
value={transaction.discount}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Rabat"
|
||||
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 shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
{errors.discount && <span className="text-red-500 text-sm">{errors.discount}</span>}
|
||||
</div>
|
||||
<button
|
||||
onClick={handleSaveChanges}
|
||||
className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
|
||||
>
|
||||
Zapisz zmiany
|
||||
</button>
|
||||
<div className="mt-6 flex justify-between">
|
||||
<button
|
||||
onClick={handleSaveChanges}
|
||||
className="bg-gradient-to-r from-green-500 to-green-700 text-white font-bold py-2 px-4 rounded-lg shadow-md hover:from-green-600 hover:to-green-800 transition"
|
||||
>
|
||||
Zapisz zmiany
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={handleCancel}
|
||||
className="bg-gradient-to-r from-red-500 to-red-700 text-white font-bold py-2 px-4 rounded-lg shadow-md hover:from-red-600 hover:to-red-800 transition ml-4"
|
||||
>
|
||||
Anuluj
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
export default EdycjaTransakcji;
|
||||
|
@ -9,6 +9,7 @@ const Harmonogram = () => {
|
||||
const [manualDateChange, setManualDateChange] = useState(false);
|
||||
const [isWorking, setIsWorking] = useState(false);
|
||||
const [selectedDay, setSelectedDay] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const token = localStorage.getItem('token');
|
||||
@ -20,7 +21,7 @@ const Harmonogram = () => {
|
||||
if (!manualDateChange) {
|
||||
setCurrentDate(new Date());
|
||||
}
|
||||
}, 1000);
|
||||
}, 10000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [manualDateChange]);
|
||||
@ -31,7 +32,6 @@ const Harmonogram = () => {
|
||||
headers: { Authorization: `Bearer ${localStorage.getItem('token')}` },
|
||||
});
|
||||
setWorkdays(response.data);
|
||||
console.log(response.data);
|
||||
} catch (error) {
|
||||
console.error('Błąd podczas pobierania dni roboczych:', error);
|
||||
}
|
||||
@ -48,9 +48,7 @@ const Harmonogram = () => {
|
||||
await axios.post(
|
||||
'https://localhost:7039/api/workday/start',
|
||||
{},
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
}
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
);
|
||||
setIsWorking(true);
|
||||
} catch (error) {
|
||||
@ -69,9 +67,7 @@ const Harmonogram = () => {
|
||||
await axios.post(
|
||||
'https://localhost:7039/api/workday/stop',
|
||||
{},
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
}
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
);
|
||||
setIsWorking(false);
|
||||
} catch (error) {
|
||||
@ -94,13 +90,12 @@ const Harmonogram = () => {
|
||||
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}`;
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
@ -110,7 +105,22 @@ const Harmonogram = () => {
|
||||
}
|
||||
|
||||
for (let i = 1; i <= numberOfDaysInMonth; i++) {
|
||||
days.push(i);
|
||||
const day = new Date(displayDate.getFullYear(), displayDate.getMonth(), i);
|
||||
const formattedDate = formatDate(day);
|
||||
|
||||
const dayWork = workdays.find(workday => {
|
||||
const startDate = new Date(workday.startTime.split('T')[0]);
|
||||
const endDate = new Date(workday.endTime.split('T')[0]);
|
||||
return (
|
||||
formattedDate === workday.startTime.split('T')[0] ||
|
||||
(day >= startDate && day <= endDate)
|
||||
);
|
||||
});
|
||||
|
||||
days.push({
|
||||
number: i,
|
||||
type: dayWork ? (dayWork.absence ? 'absence' : 'working') : 'default'
|
||||
});
|
||||
}
|
||||
|
||||
setDaysInMonth(days);
|
||||
@ -118,7 +128,7 @@ const Harmonogram = () => {
|
||||
|
||||
useEffect(() => {
|
||||
generateDaysInMonth();
|
||||
}, [displayDate]);
|
||||
}, [displayDate, workdays]);
|
||||
|
||||
const changeMonth = (direction) => {
|
||||
setManualDateChange(true);
|
||||
@ -140,20 +150,68 @@ const Harmonogram = () => {
|
||||
return months[monthIndex];
|
||||
};
|
||||
|
||||
const handleDayClick = (day) => {
|
||||
const workDay = workdays.find((workday) => {
|
||||
const workDayDate = new Date(workday.startTime).toLocaleDateString();
|
||||
const selectedDayDate = new Date(displayDate.getFullYear(), displayDate.getMonth(), day).toLocaleDateString();
|
||||
return workDayDate === selectedDayDate;
|
||||
});
|
||||
setSelectedDay(workDay || null);
|
||||
};
|
||||
|
||||
const formatWorkedHours = (workedHours) => {
|
||||
const [hours, minutes, seconds] = workedHours.split(':');
|
||||
const handleDayClick = async (day) => {
|
||||
if (!day) return;
|
||||
|
||||
return `${hours}:${minutes}:${Math.floor(parseFloat(seconds)).toString().padStart(2, '0')}`;
|
||||
const selectedDate = new Date(displayDate.getFullYear(), displayDate.getMonth(), day, 0, 0, 0, 0);
|
||||
const formattedDate = formatDate(selectedDate);
|
||||
|
||||
const dayStatus = workdays.find(workday => {
|
||||
const startDate = new Date(workday.startTime.split('T')[0]);
|
||||
const endDate = new Date(workday.endTime.split('T')[0]);
|
||||
|
||||
return (
|
||||
formattedDate === workday.startTime.split('T')[0] ||
|
||||
(selectedDate >= startDate && selectedDate <= endDate)
|
||||
);
|
||||
});
|
||||
|
||||
console.log("Selected Date:", formattedDate);
|
||||
console.log("Day Status:", dayStatus);
|
||||
|
||||
if (dayStatus) {
|
||||
if (dayStatus.absence && dayStatus.absence.trim() !== "") {
|
||||
setSelectedDay({
|
||||
date: formattedDate,
|
||||
dayOfWeek: formatDayOfWeek(selectedDate),
|
||||
absence: dayStatus.absence,
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await axios.get(
|
||||
`https://localhost:7039/api/Workday/user/day/info/${formattedDate}`,
|
||||
{ headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } }
|
||||
);
|
||||
const workdayDetails = response.data;
|
||||
|
||||
setSelectedDay({
|
||||
date: formattedDate,
|
||||
dayOfWeek: formatDayOfWeek(selectedDate),
|
||||
workdayDetails: workdayDetails.workdayDetails,
|
||||
totalWorkedHours: workdayDetails.totalWorkedHours,
|
||||
absence: null,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Błąd podczas pobierania danych dnia roboczego:", error);
|
||||
setSelectedDay({
|
||||
date: formattedDate,
|
||||
dayOfWeek: formatDayOfWeek(selectedDate),
|
||||
absence: "Błąd pobierania danych",
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setSelectedDay({
|
||||
date: formattedDate,
|
||||
dayOfWeek: formatDayOfWeek(selectedDate),
|
||||
absence: "Brak danych o tym dniu",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-6">
|
||||
@ -162,69 +220,87 @@ const Harmonogram = () => {
|
||||
<p>{formatDate(currentDate)} - {formatDayOfWeek(currentDate)}</p>
|
||||
<p>{formatTime(currentDate)}</p>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
className={`py-2 px-4 rounded text-white ${isWorking ? 'bg-red-500 hover:bg-red-700' : 'bg-green-500 hover:bg-green-700'}`}
|
||||
onClick={isWorking ? stopWork : startWork}
|
||||
>
|
||||
{isWorking ? 'Zakończ pracę' : 'Rozpocznij pracę'}
|
||||
</button>
|
||||
</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')}
|
||||
className={`py-2 px-4 rounded text-white ${isWorking ? 'bg-red-500 hover:bg-red-700' : 'bg-green-500 hover:bg-green-700'}`}
|
||||
onClick={isWorking ? stopWork : startWork}
|
||||
>
|
||||
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
|
||||
{isWorking ? 'Zakończ pracę' : 'Rozpocznij pracę'}
|
||||
</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'}
|
||||
${workdays.some(workday => new Date(workday.startTime).getDate() === day) && 'bg-green-200'}`}
|
||||
style={{
|
||||
boxShadow: day ? '0 4px 6px rgba(0, 0, 0, 0.1)' : 'none',
|
||||
}}
|
||||
onClick={() => day && handleDayClick(day)}
|
||||
>
|
||||
{day}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{selectedDay && (
|
||||
<div className="modal fixed inset-0 flex justify-center items-center bg-gray-800 bg-opacity-50 z-50">
|
||||
<div className="bg-white p-6 rounded-lg shadow-lg">
|
||||
<h2 className="text-2xl font-bold mb-4">Godziny pracy</h2>
|
||||
<p><strong>Dzień:</strong> {formatDate(new Date(selectedDay.startTime))}</p>
|
||||
<p><strong>Start:</strong> {formatTime(new Date(selectedDay.startTime))}</p>
|
||||
<p><strong>Stop:</strong> {formatTime(new Date(selectedDay.endTime))}</p>
|
||||
<p><strong>Godziny pracy:</strong> {formatWorkedHours(selectedDay.workedHours)}</p>
|
||||
<button
|
||||
onClick={() => setSelectedDay(null)}
|
||||
className="bg-blue-500 text-white py-2 px-4 rounded mt-4"
|
||||
>
|
||||
Zamknij
|
||||
<div className="flex gap-6">
|
||||
<div className="w-2/3">
|
||||
<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 && day.type === 'working' ? 'bg-green-500' : ''}
|
||||
${day && day.type === 'absence' ? 'bg-red-500' : ''}
|
||||
hover:bg-gray-300 transition duration-150 ease-in-out`}
|
||||
onClick={() => handleDayClick(day?.number)}
|
||||
>
|
||||
{day?.number || ''}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="w-1/3 bg-gray-100 p-4 rounded-lg">
|
||||
{loading ? (
|
||||
<p className="text-center text-blue-500">Ładowanie danych...</p>
|
||||
) : selectedDay ? (
|
||||
<div className="text-center">
|
||||
<h2 className="text-xl font-bold mb-4">
|
||||
Szczegóły dnia {selectedDay.date} ({selectedDay.dayOfWeek})
|
||||
</h2>
|
||||
{selectedDay.absence && selectedDay.absence.trim() !== "" ? (
|
||||
<p>{selectedDay.absence}</p>
|
||||
) : selectedDay.workdayDetails && selectedDay.workdayDetails.length > 0 ? (
|
||||
selectedDay.workdayDetails.map((detail, index) => {
|
||||
const formattedStartTime = new Date(detail.startTime).toLocaleTimeString([], {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
const formattedEndTime = new Date(detail.endTime).toLocaleTimeString([], {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
const workedHours = new Date(`1970-01-01T${detail.workedHours}Z`)
|
||||
.toISOString()
|
||||
.substr(11, 8);
|
||||
|
||||
return (
|
||||
<div key={index} className="mb-2">
|
||||
<p><strong>Start:</strong> {formattedStartTime}</p>
|
||||
<p><strong>Koniec:</strong> {formattedEndTime}</p>
|
||||
<p><strong>Przepracowane godziny:</strong> {workedHours}</p>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<p className="text-gray-500">Brak danych o tym dniu.</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-center text-gray-500">Wybierz dzień, aby zobaczyć szczegóły.</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -56,36 +56,38 @@ const ListaProduktow = ({ onAdd }) => {
|
||||
|
||||
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">
|
||||
<div className="flex items-center justify-between py-6 px-8 bg-gradient-to-r from-blue-500 to-teal-500 rounded-xl shadow-md mb-6">
|
||||
<h1 className="text-white text-4xl font-semibold">Katalog Produktów oraz usług</h1>
|
||||
<button onClick={onAdd} className="bg-gradient-to-r from-green-500 to-green-700 text-white py-2 px-4 rounded-lg hover:from-green-600 hover:to-green-800 transition">
|
||||
Dodaj Produkt
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<table className="w-full border border-gray-300">
|
||||
<thead className="bg-gray-200">
|
||||
<table className="w-full border border-gray-300 rounded-lg shadow-lg overflow-hidden">
|
||||
<thead className="bg-gray-100 text-gray-700">
|
||||
<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>
|
||||
<th className="p-3 text-left">ID</th>
|
||||
<th className="p-3 text-left">Produkt</th>
|
||||
<th className="p-3 text-left">Opis</th>
|
||||
<th className="p-3 text-left">Cena</th>
|
||||
<th className="p-3 text-center">Dostępność</th>
|
||||
<th className="p-3 text-center">Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody className="text-gray-600">
|
||||
{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.type === 0 ? "" : product.availability}</td>
|
||||
<td className="p-2 border">
|
||||
<tr key={product.id} className="hover:bg-gray-50 transition-colors">
|
||||
<td className="p-3">{product.id}</td>
|
||||
<td className="p-3">{product.name}</td>
|
||||
<td className="p-3">{product.description}</td>
|
||||
<td className="p-3">{parseFloat(product.price).toFixed(2)} zł</td>
|
||||
<td className="p-3 text-center">
|
||||
{product.type === 0 ? "" : product.availability}
|
||||
</td>
|
||||
<td className="p-3 flex justify-center items-center space-x-2">
|
||||
<button
|
||||
onClick={() => handleEditProduct(product.id)}
|
||||
className="mr-2 bg-blue-500 text-white py-2 px-4 rounded"
|
||||
className="bg-gradient-to-r from-blue-500 to-blue-700 text-white py-2 px-4 rounded-lg hover:from-blue-600 hover:to-blue-800 transition"
|
||||
>
|
||||
<img src={editIcon} alt="Edytuj" className="inline w-5 mr-2" />
|
||||
Edytuj
|
||||
@ -95,17 +97,18 @@ const ListaProduktow = ({ onAdd }) => {
|
||||
setDeleteProductId(product.id);
|
||||
setShowModal(true);
|
||||
}}
|
||||
className="bg-red-500 text-white py-2 px-4 rounded"
|
||||
className="bg-gradient-to-r from-red-500 to-red-700 text-white py-2 px-4 rounded-lg hover:from-red-600 hover:to-red-800 transition"
|
||||
>
|
||||
<img src={koszIcon} alt="Usuń" className="inline w-5 mr-2" />
|
||||
Usuń
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
{showModal && (
|
||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-50 flex justify-center items-center z-50">
|
||||
|
@ -87,56 +87,56 @@ const ListaTransakcji = ({ onAdd}) => {
|
||||
|
||||
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">
|
||||
<div className="flex items-center justify-between py-6 px-8 bg-gradient-to-r from-blue-500 to-teal-500 rounded-xl shadow-md mb-6">
|
||||
<h1 className="text-white text-4xl font-semibold">Lista Transakcji</h1>
|
||||
<button onClick={onAdd} className="bg-gradient-to-r from-green-500 to-green-700 text-white py-2 px-4 rounded-lg hover:from-green-600 hover:to-green-800 transition">
|
||||
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">
|
||||
<table className="w-full border border-gray-300 rounded-lg shadow-lg overflow-hidden">
|
||||
<thead className="bg-gray-100 text-gray-700">
|
||||
<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="p-3 text-left">ID</th>
|
||||
<th className="p-3 text-left">Data</th>
|
||||
<th className="p-3 text-left">Produkt</th>
|
||||
<th className="p-3 text-left">Ilość</th>
|
||||
<th className="p-3 text-left">Kwota</th>
|
||||
<th className="p-3 text-left">Sposób płatności</th>
|
||||
<th className="p-3 text-center">Nr. Pracownika</th>
|
||||
<th className="p-3 text-center">Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody className="text-gray-600">
|
||||
{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">
|
||||
<tr key={transaction.id} className="hover:bg-gray-50 transition-colors">
|
||||
<td className="p-3">{transaction.id}</td>
|
||||
<td className="p-3">{formatDate(transaction.date)}</td>
|
||||
<td className="p-3">
|
||||
{transaction.transactionProducts.map(product => (
|
||||
<div key={product.id}>{product.product.name}</div>
|
||||
))}
|
||||
</td>
|
||||
<td className="border border-gray-300 p-2">
|
||||
<td className="p-3">
|
||||
{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">
|
||||
<td className="p-3">{formatPrice(transaction.totalPrice)}</td>
|
||||
<td className="p-3">{transaction.paymentType}</td>
|
||||
<td className="p-3 text-center">{transaction.employeeId}</td>
|
||||
<td className="p-3 flex justify-center space-x-2">
|
||||
<button
|
||||
onClick={() => handleEditTransaction(transaction.id)}
|
||||
className="mr-2 bg-blue-500 text-white py-2 px-4 rounded"
|
||||
className="bg-gradient-to-r from-blue-500 to-blue-700 text-white py-2 px-4 rounded-lg hover:from-blue-600 hover:to-blue-800 transition"
|
||||
>
|
||||
<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"
|
||||
className="bg-gradient-to-r from-red-500 to-red-700 text-white py-2 px-4 rounded-lg hover:from-red-600 hover:to-red-800 transition"
|
||||
>
|
||||
<img src={koszIcon} alt="Usuń" className="inline w-5 mr-2" />
|
||||
Usuń
|
||||
@ -149,6 +149,8 @@ const ListaTransakcji = ({ onAdd}) => {
|
||||
</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">
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import WidokHarmonogramu from './WidokHarmonogramu';
|
||||
|
||||
const PanelAdministratora = () => {
|
||||
const [selectedOption, setSelectedOption] = useState('harmonogramy');
|
||||
@ -9,7 +10,9 @@ const PanelAdministratora = () => {
|
||||
const [emails, setEmails] = useState([]);
|
||||
const [selectedEmail, setSelectedEmail] = useState('');
|
||||
const [workdays, setWorkdays] = useState([]);
|
||||
const [absenceType, setAbsenceType] = useState('');
|
||||
|
||||
// Funkcja pobierania emaili
|
||||
const fetchEmails = async () => {
|
||||
try {
|
||||
const response = await axios.get('https://localhost:7039/api/user/emails', {
|
||||
@ -20,6 +23,31 @@ const PanelAdministratora = () => {
|
||||
console.error('Błąd podczas pobierania emaili:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Funkcja dodawania absencji
|
||||
const addAbsence = async () => {
|
||||
if (!selectedEmail || !absenceType || !startDate || !endDate) {
|
||||
alert("Wszystkie pola muszą być wypełnione!");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await axios.post('https://localhost:7039/api/Workday/user/absence/add', {
|
||||
userEmail: selectedEmail,
|
||||
absenceType: absenceType,
|
||||
startTime: startDate,
|
||||
endTime: endDate,
|
||||
}, {
|
||||
headers: { Authorization: `Bearer ${localStorage.getItem('token')}` },
|
||||
});
|
||||
|
||||
console.log("Absencja wysłana");
|
||||
} catch (error) {
|
||||
console.error('Błąd podczas dodawania absencji:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Funkcja pobierania raportu
|
||||
const downloadReport = async () => {
|
||||
if (!reportType || !startDate || !endDate) {
|
||||
alert("Wszystkie pola muszą być wypełnione!");
|
||||
@ -34,7 +62,7 @@ const PanelAdministratora = () => {
|
||||
endDate: endDate,
|
||||
},
|
||||
headers: { Authorization: `Bearer ${localStorage.getItem('token')}` },
|
||||
responseType: 'blob', // Ustawienie typu odpowiedzi jako blob (do pobrania pliku)
|
||||
responseType: 'blob',
|
||||
});
|
||||
|
||||
const file = new Blob([response.data], { type: 'application/pdf' });
|
||||
@ -47,6 +75,8 @@ const PanelAdministratora = () => {
|
||||
console.error('Błąd podczas pobierania raportu:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Funkcja pobierania harmonogramów
|
||||
const fetchWorkdays = async (userEmail) => {
|
||||
if (!userEmail) {
|
||||
setWorkdays([]);
|
||||
@ -63,6 +93,7 @@ const PanelAdministratora = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// UseEffect do pobierania danych emaili przy pierwszym renderowaniu
|
||||
useEffect(() => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
@ -70,6 +101,7 @@ const PanelAdministratora = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
// UseEffect do pobierania harmonogramu przy zmianie emaila
|
||||
useEffect(() => {
|
||||
if (selectedEmail) {
|
||||
fetchWorkdays(selectedEmail);
|
||||
@ -78,221 +110,196 @@ const PanelAdministratora = () => {
|
||||
|
||||
return (
|
||||
<div className='p-10 ml-11'>
|
||||
<div className='h-20 text-2xl'>
|
||||
Panel Administratora
|
||||
</div>
|
||||
|
||||
<div className='flex h-20'>
|
||||
<div className='flex items-center justify-between py-6 px-8 bg-gradient-to-r from-blue-500 to-teal-500 rounded-xl shadow-md mb-6'>
|
||||
<h1 className="text-white text-4xl font-semibold">Panel Administratora</h1>
|
||||
<div className="mr-10 text-lg flex">
|
||||
<div className='px-10'>
|
||||
<button onClick={() => setSelectedOption('harmonogramy')} className={selectedOption === 'harmonogramy' ? 'text-blue-500 font-bold' : ''}>Harmonogramy</button>
|
||||
<button
|
||||
onClick={() => setSelectedOption('harmonogramy')}
|
||||
className={`
|
||||
${selectedOption === 'harmonogramy' ? 'text-white font-bold' : 'text-gray-200'}
|
||||
hover:text-white hover:bg-blue-600 hover:rounded-lg transition duration-300 ease-in-out
|
||||
`}>
|
||||
Harmonogramy
|
||||
</button>
|
||||
</div>
|
||||
<div className='px-10'>
|
||||
<button
|
||||
onClick={() => setSelectedOption('absencje')}
|
||||
className={`
|
||||
${selectedOption === 'absencje' ? 'text-white font-bold' : 'text-gray-200'}
|
||||
hover:text-white hover:bg-teal-600 hover:rounded-lg transition duration-300 ease-in-out
|
||||
`}>
|
||||
Absencje
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={() => setSelectedOption('raporty')} className={selectedOption === 'raporty' ? 'text-blue-500 font-bold' : ''}>Raporty</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{selectedOption === 'raporty' && (
|
||||
<div>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="reportType" className="mr-4">Wybierz typ raportu:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="reportType"
|
||||
value={reportType}
|
||||
onChange={(e) => setReportType(e.target.value)}
|
||||
className="p-2 border"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="startDate" className="mr-4">Wybierz datę początkową:</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
id="startDate"
|
||||
value={startDate}
|
||||
onChange={(e) => setStartDate(e.target.value)}
|
||||
className="p-2 border"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="endDate" className="mr-4">Wybierz datę końcową:</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
id="endDate"
|
||||
value={endDate}
|
||||
onChange={(e) => setEndDate(e.target.value)}
|
||||
className="p-2 border"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={downloadReport}
|
||||
className="bg-blue-500 text-white py-2 px-4 rounded mt-4"
|
||||
>
|
||||
Pobierz Raport
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedOption === 'harmonogramy' && (
|
||||
<div>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="email" className="mr-4">Wybierz email:</label>
|
||||
<select
|
||||
id="email"
|
||||
value={selectedEmail}
|
||||
onChange={(e) => setSelectedEmail(e.target.value)}
|
||||
className="p-2 border"
|
||||
>
|
||||
<option value="">Wybierz...</option>
|
||||
{emails.map((email) => (
|
||||
<option key={email} value={email}>{email}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
{selectedEmail && (
|
||||
<WidokHarmonogramu workdays={workdays} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const WidokHarmonogramu = ({ workdays }) => {
|
||||
const [displayDate, setDisplayDate] = useState(new Date());
|
||||
const [daysInMonth, setDaysInMonth] = useState([]);
|
||||
const [manualDateChange, setManualDateChange] = useState(false);
|
||||
const [selectedDay, setSelectedDay] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
generateDaysInMonth();
|
||||
}, [displayDate]);
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
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];
|
||||
};
|
||||
|
||||
const handleDayClick = (day) => {
|
||||
const workDay = workdays.find((workday) => {
|
||||
const workDayDate = new Date(workday.startTime).toLocaleDateString();
|
||||
const selectedDayDate = new Date(displayDate.getFullYear(), displayDate.getMonth(), day).toLocaleDateString();
|
||||
return workDayDate === selectedDayDate;
|
||||
});
|
||||
setSelectedDay(workDay || null);
|
||||
};
|
||||
|
||||
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 formatWorkedHours = (workedHours) => {
|
||||
const [hours, minutes, seconds] = workedHours.split(':');
|
||||
|
||||
return `${hours}:${minutes}:${Math.floor(parseFloat(seconds)).toString().padStart(2, '0')}`;
|
||||
};
|
||||
const formatTime = (date) => {
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
return `${hours}:${minutes}`;
|
||||
};
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-6">
|
||||
<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'}
|
||||
${workdays.some(workday => new Date(workday.startTime).getDate() === day) && 'bg-green-200'}`}
|
||||
style={{
|
||||
boxShadow: day ? '0 4px 6px rgba(0, 0, 0, 0.1)' : 'none',
|
||||
}}
|
||||
onClick={() => day && handleDayClick(day)}
|
||||
>
|
||||
{day}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{selectedDay && (
|
||||
<div className="modal fixed inset-0 flex justify-center items-center bg-gray-800 bg-opacity-50 z-50">
|
||||
<div className="bg-white p-6 rounded-lg shadow-lg">
|
||||
<h2 className="text-2xl font-bold mb-4">Godziny pracy</h2>
|
||||
<p><strong>Dzień:</strong> {formatDate(new Date(selectedDay.startTime))}</p>
|
||||
<p><strong>Start:</strong> {formatTime(new Date(selectedDay.startTime))}</p>
|
||||
<p><strong>Stop:</strong> {formatTime(new Date(selectedDay.endTime))}</p>
|
||||
<p><strong>Godziny pracy:</strong> {formatWorkedHours(selectedDay.workedHours)}</p>
|
||||
<button
|
||||
onClick={() => setSelectedDay(null)}
|
||||
className="bg-blue-500 text-white py-2 px-4 rounded mt-4"
|
||||
>
|
||||
Zamknij
|
||||
<button
|
||||
onClick={() => setSelectedOption('raporty')}
|
||||
className={`
|
||||
${selectedOption === 'raporty' ? 'text-white font-bold' : 'text-gray-200'}
|
||||
hover:text-white hover:bg-indigo-600 hover:rounded-lg transition duration-300 ease-in-out
|
||||
`}>
|
||||
Raporty
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{selectedOption === 'raporty' && (
|
||||
<div className="flex justify-center items-start pt-10">
|
||||
<div className="bg-white p-8 rounded-xl shadow-lg w-full max-w-md">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-2xl font-semibold text-center mb-4">Pobierz Raport</h2>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="reportType" className="block text-lg font-medium text-gray-700 mb-2">Wybierz typ raportu:</label>
|
||||
<select
|
||||
id="reportType"
|
||||
value={reportType}
|
||||
onChange={(e) => setReportType(e.target.value)}
|
||||
className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">Wybierz...</option>
|
||||
<option value="expenses">Wydatki</option>
|
||||
<option value="transactions">Transakcje</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="startDate" className="block text-lg font-medium text-gray-700 mb-2">Wybierz datę początkową:</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
id="startDate"
|
||||
value={startDate}
|
||||
onChange={(e) => setStartDate(e.target.value)}
|
||||
className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<label htmlFor="endDate" className="block text-lg font-medium text-gray-700 mb-2">Wybierz datę końcową:</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
id="endDate"
|
||||
value={endDate}
|
||||
onChange={(e) => setEndDate(e.target.value)}
|
||||
className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={downloadReport}
|
||||
className="w-full bg-blue-500 text-white py-3 px-4 rounded-lg mt-4 hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition duration-300"
|
||||
>
|
||||
Pobierz Raport
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
)}
|
||||
|
||||
{selectedOption === 'harmonogramy' && (
|
||||
<div className="flex justify-center items-start pt-10">
|
||||
<div className="bg-white p-8 rounded-xl shadow-lg w-full">
|
||||
<div className="mb-6">
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="email" className="block text-lg font-medium text-gray-700 mb-2">Wybierz email:</label>
|
||||
<select
|
||||
id="email"
|
||||
value={selectedEmail}
|
||||
onChange={(e) => setSelectedEmail(e.target.value)}
|
||||
className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">Wybierz...</option>
|
||||
{emails.map((email) => (
|
||||
<option key={email} value={email}>{email}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
{selectedEmail && (
|
||||
<WidokHarmonogramu email={selectedEmail} workdays={workdays} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedOption === 'absencje' && (
|
||||
<div className="flex justify-center items-start pt-10">
|
||||
<div className="bg-white p-8 rounded-xl shadow-lg w-full max-w-md">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-2xl font-semibold text-center mb-4">Dodaj Absencję</h2>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="email" className="block text-lg font-medium text-gray-700 mb-2">Wybierz email:</label>
|
||||
<select
|
||||
id="email"
|
||||
value={selectedEmail}
|
||||
onChange={(e) => setSelectedEmail(e.target.value)}
|
||||
className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">Wybierz...</option>
|
||||
{emails.map((email) => (
|
||||
<option key={email} value={email}>{email}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="absenceType" className="block text-lg font-medium text-gray-700 mb-2">Typ absencji:</label>
|
||||
<select
|
||||
id="absenceType"
|
||||
value={absenceType}
|
||||
onChange={(e) => setAbsenceType(e.target.value)}
|
||||
className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">Wybierz...</option>
|
||||
<option value="sick">Chorobowe</option>
|
||||
<option value="vacation">Urlop</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="startDate" className="block text-lg font-medium text-gray-700 mb-2">Wybierz datę początkową:</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
id="startDate"
|
||||
value={startDate}
|
||||
onChange={(e) => setStartDate(e.target.value)}
|
||||
className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<label htmlFor="endDate" className="block text-lg font-medium text-gray-700 mb-2">Wybierz datę końcową:</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
id="endDate"
|
||||
value={endDate}
|
||||
onChange={(e) => setEndDate(e.target.value)}
|
||||
className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={addAbsence}
|
||||
className="w-full bg-blue-500 text-white py-3 px-4 rounded-lg mt-4 hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition duration-300"
|
||||
>
|
||||
Dodaj Absencję
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PanelAdministratora;
|
||||
export default PanelAdministratora;
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import koszIcon from "../icons/kosz.png";
|
||||
import ConfirmationModal from './ConfirmationModal';
|
||||
|
||||
const Raporty = () => {
|
||||
const [fromDate, setFromDate] = useState('');
|
||||
@ -9,6 +8,7 @@ const Raporty = () => {
|
||||
const [error, setError] = useState(null);
|
||||
const [reports, setReports] = useState([]);
|
||||
const [deleteReportId, setDeleteReportId] = useState(null);
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||
|
||||
const fetchReports = async () => {
|
||||
const token = localStorage.getItem('token');
|
||||
@ -28,11 +28,13 @@ const Raporty = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const openDeleteConfirmation = (transactionId) => {
|
||||
setDeleteReportId(transactionId);
|
||||
const openDeleteConfirmation = (reportId) => {
|
||||
setDeleteReportId(reportId);
|
||||
setShowDeleteModal(true);
|
||||
};
|
||||
|
||||
const closeDeleteConfirmation = () => {
|
||||
setShowDeleteModal(false);
|
||||
setDeleteReportId(null);
|
||||
};
|
||||
|
||||
@ -77,7 +79,7 @@ const Raporty = () => {
|
||||
}
|
||||
});
|
||||
fetchReports();
|
||||
setDeleteReportId(null);
|
||||
closeDeleteConfirmation();
|
||||
} catch (error) {
|
||||
console.error('Błąd podczas usuwania raportu:', error);
|
||||
if (error.response && error.response.data) {
|
||||
@ -96,67 +98,116 @@ const Raporty = () => {
|
||||
|
||||
return (
|
||||
<div className="p-10 ml-11">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="h-20 text-5xl ml-1">
|
||||
Generowanie raportów
|
||||
<div className="flex items-center justify-between py-6 px-8 bg-gradient-to-r from-blue-500 to-teal-500 rounded-xl shadow-md mb-6">
|
||||
<h1 className="text-white text-4xl font-semibold">Generowanie raportów</h1>
|
||||
</div>
|
||||
|
||||
<div className="bg-white shadow-lg p-8 rounded-xl max-w-3xl mx-auto">
|
||||
<div className="mb-6 flex items-center space-x-6">
|
||||
<div className="flex-1">
|
||||
<label htmlFor="fromDate" className="block text-lg font-medium text-gray-700 mb-2">Od:</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
id="fromDate"
|
||||
value={fromDate}
|
||||
onChange={(e) => setFromDate(e.target.value)}
|
||||
className="w-full px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex-1">
|
||||
<label htmlFor="toDate" className="block text-lg font-medium text-gray-700 mb-2">Do:</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
id="toDate"
|
||||
value={toDate}
|
||||
onChange={(e) => setToDate(e.target.value)}
|
||||
className="w-full px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleGenerateReport}
|
||||
className="w-full bg-gradient-to-r from-blue-500 to-blue-700 text-white font-semibold py-3 px-6 rounded-lg shadow-md hover:from-blue-600 hover:to-blue-800 transition duration-300 ease-in-out"
|
||||
>
|
||||
Generuj raport
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div className="mt-5">
|
||||
<label htmlFor="fromDate" className="mr-3">Od:</label>
|
||||
<input type="datetime-local" id="fromDate" value={fromDate} onChange={(e) => setFromDate(e.target.value)} className="mr-5" />
|
||||
<label htmlFor="toDate" className="mr-3">Do:</label>
|
||||
<input type="datetime-local" id="toDate" value={toDate} onChange={(e) => setToDate(e.target.value)} className="mr-5" />
|
||||
<button onClick={handleGenerateReport} className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Generuj</button>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<h2 className="text-2xl font-bold mb-4">Raporty</h2>
|
||||
<table className="w-full border-collapse border border-gray-300">
|
||||
<thead className="bg-gray-200 top-0 z-10">
|
||||
<table className="min-w-full border-collapse table-auto shadow-lg">
|
||||
<thead className="bg-gray-200 text-gray-700">
|
||||
<tr>
|
||||
<th className="border border-gray-300 p-2">ID</th>
|
||||
<th className="border border-gray-300 p-2">Data od</th>
|
||||
<th className="border border-gray-300 p-2">Data do</th>
|
||||
<th className="border border-gray-300 p-2">Suma dochodów</th>
|
||||
<th className="border border-gray-300 p-2">Suma wydatków</th>
|
||||
<th className="border border-gray-300 p-2">Bilans</th>
|
||||
<th className="border border-gray-300 p-2"></th>
|
||||
<th className="p-2 text-left">ID</th>
|
||||
<th className="p-2 text-left">Data od</th>
|
||||
<th className="p-2 text-left">Data do</th>
|
||||
<th className="p-2 text-left">Suma dochodów</th>
|
||||
<th className="p-2 text-left">Suma wydatków</th>
|
||||
<th className="p-2 text-left">Bilans</th>
|
||||
<th className="p-2 text-center"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{reports.map(report => (
|
||||
<tr key={report.id}>
|
||||
<td className="border border-gray-300 p-2">{report.id}</td>
|
||||
<td className="border border-gray-300 p-2">{formatDate(report.fromDate)}</td>
|
||||
<td className="border border-gray-300 p-2">{formatDate(report.toDate)}</td>
|
||||
<td className="border border-gray-300 p-2">{report.totalIncome}</td>
|
||||
<td className="border border-gray-300 p-2">{report.totalExpenses}</td>
|
||||
<td className="border border-gray-300 p-2">{report.totalBalance}</td>
|
||||
<td className="border border-gray-300 p-2">
|
||||
<button onClick={() => openDeleteConfirmation(report.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>
|
||||
{reports.map((report) => (
|
||||
<tr key={report.id} className="hover:bg-gray-50">
|
||||
<td className="p-2">{report.id}</td>
|
||||
<td className="p-2">{formatDate(report.fromDate)}</td>
|
||||
<td className="p-2">{formatDate(report.toDate)}</td>
|
||||
<td className="p-2">{report.totalIncome}</td>
|
||||
<td className="p-2">{report.totalExpenses}</td>
|
||||
<td className="p-2">{report.totalBalance}</td>
|
||||
<td className="p-2 text-center">
|
||||
<button
|
||||
onClick={() => openDeleteConfirmation(report.id)}
|
||||
className="bg-gradient-to-r from-red-500 to-red-700 text-white py-2 px-4 rounded-lg hover:from-red-600 hover:to-red-800 transition"
|
||||
>
|
||||
<img src={koszIcon} alt="Usuń" className="inline w-5 mr-2" /> Usuń
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</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">
|
||||
<button
|
||||
onClick={() => window.location.reload()}
|
||||
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-lg"
|
||||
>
|
||||
Zamknij
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{deleteReportId && (
|
||||
<ConfirmationModal
|
||||
message="Czy na pewno chcesz usunąć ten raport?"
|
||||
onCancel={closeDeleteConfirmation}
|
||||
onConfirm={() => handleDeleteReport(deleteReportId)}
|
||||
/>
|
||||
|
||||
{showDeleteModal && (
|
||||
<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 raport?</h2>
|
||||
<div className="flex justify-between">
|
||||
<button
|
||||
onClick={() => handleDeleteReport(deleteReportId)}
|
||||
className="bg-red-500 text-white py-2 px-4 rounded"
|
||||
>
|
||||
Tak
|
||||
</button>
|
||||
<button
|
||||
onClick={closeDeleteConfirmation}
|
||||
className="bg-gray-500 text-white py-2 px-4 rounded"
|
||||
>
|
||||
Anuluj
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,135 +1,221 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
|
||||
const WidokHarmonogramu = ({ workdays }) => {
|
||||
const [displayDate, setDisplayDate] = useState(new Date());
|
||||
const [daysInMonth, setDaysInMonth] = useState([]);
|
||||
const [manualDateChange, setManualDateChange] = useState(false);
|
||||
const [selectedDay, setSelectedDay] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
generateDaysInMonth();
|
||||
}, [displayDate]);
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
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];
|
||||
};
|
||||
|
||||
const handleDayClick = (day) => {
|
||||
const workDay = workdays.find((workday) => {
|
||||
const workDayDate = new Date(workday.startTime).toLocaleDateString();
|
||||
const selectedDayDate = new Date(displayDate.getFullYear(), displayDate.getMonth(), day).toLocaleDateString();
|
||||
return workDayDate === selectedDayDate;
|
||||
const [currentDate, setCurrentDate] = useState(new Date());
|
||||
const [displayDate, setDisplayDate] = useState(new Date());
|
||||
const [daysInMonth, setDaysInMonth] = useState([]);
|
||||
const [manualDateChange, setManualDateChange] = useState(false);
|
||||
const [isWorking, setIsWorking] = useState(false);
|
||||
const [selectedDay, setSelectedDay] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
generateDaysInMonth();
|
||||
}, [displayDate, workdays]);
|
||||
|
||||
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++) {
|
||||
const day = new Date(displayDate.getFullYear(), displayDate.getMonth(), i);
|
||||
const formattedDate = formatDate(day);
|
||||
|
||||
const dayWork = workdays.find(workday => {
|
||||
const startDate = new Date(workday.startTime.split('T')[0]);
|
||||
const endDate = new Date(workday.endTime.split('T')[0]);
|
||||
return (
|
||||
formattedDate === workday.startTime.split('T')[0] ||
|
||||
(day >= startDate && day <= endDate)
|
||||
);
|
||||
});
|
||||
setSelectedDay(workDay || null);
|
||||
};
|
||||
|
||||
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 formatWorkedHours = (workedHours) => {
|
||||
const [hours, minutes, seconds] = workedHours.split(':');
|
||||
|
||||
return `${hours}:${minutes}:${Math.floor(parseFloat(seconds)).toString().padStart(2, '0')}`;
|
||||
};
|
||||
const formatTime = (date) => {
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
return `${hours}:${minutes}`;
|
||||
};
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-6">
|
||||
<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'}
|
||||
${workdays.some(workday => new Date(workday.startTime).getDate() === day) && 'bg-green-200'}`}
|
||||
style={{
|
||||
boxShadow: day ? '0 4px 6px rgba(0, 0, 0, 0.1)' : 'none',
|
||||
}}
|
||||
onClick={() => day && handleDayClick(day)}
|
||||
>
|
||||
{day}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{selectedDay && (
|
||||
<div className="modal fixed inset-0 flex justify-center items-center bg-gray-800 bg-opacity-50 z-50">
|
||||
<div className="bg-white p-6 rounded-lg shadow-lg">
|
||||
<h2 className="text-2xl font-bold mb-4">Godziny pracy</h2>
|
||||
<p><strong>Dzień:</strong> {formatDate(new Date(selectedDay.startTime))}</p>
|
||||
<p><strong>Start:</strong> {formatTime(new Date(selectedDay.startTime))}</p>
|
||||
<p><strong>Stop:</strong> {formatTime(new Date(selectedDay.endTime))}</p>
|
||||
<p><strong>Godziny pracy:</strong> {formatWorkedHours(selectedDay.workedHours)}</p>
|
||||
<button
|
||||
onClick={() => setSelectedDay(null)}
|
||||
className="bg-blue-500 text-white py-2 px-4 rounded mt-4"
|
||||
>
|
||||
Zamknij
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
days.push({
|
||||
number: i,
|
||||
type: dayWork ? (dayWork.absence ? 'absence' : 'working') : 'default'
|
||||
});
|
||||
}
|
||||
|
||||
setDaysInMonth(days);
|
||||
};
|
||||
export default WidokHarmonogramu;
|
||||
|
||||
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];
|
||||
};
|
||||
|
||||
const handleDayClick = async (day) => {
|
||||
if (!day) return;
|
||||
|
||||
const selectedDate = new Date(displayDate.getFullYear(), displayDate.getMonth(), day, 0, 0, 0, 0);
|
||||
const formattedDate = formatDate(selectedDate);
|
||||
|
||||
const dayStatus = workdays.find(workday => {
|
||||
const startDate = new Date(workday.startTime.split('T')[0]);
|
||||
const endDate = new Date(workday.endTime.split('T')[0]);
|
||||
|
||||
return (
|
||||
formattedDate === workday.startTime.split('T')[0] ||
|
||||
(selectedDate >= startDate && selectedDate <= endDate)
|
||||
);
|
||||
});
|
||||
|
||||
console.log("Selected Date:", formattedDate);
|
||||
console.log("Day Status:", dayStatus);
|
||||
|
||||
if (dayStatus) {
|
||||
if (dayStatus.absence && dayStatus.absence.trim() !== "") {
|
||||
setSelectedDay({
|
||||
date: formattedDate,
|
||||
dayOfWeek: formatDayOfWeek(selectedDate),
|
||||
absence: dayStatus.absence,
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`https://localhost:7039/api/Workday/user/day/info/${formattedDate}`,
|
||||
{ headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } }
|
||||
);
|
||||
const workdayDetails = response.data;
|
||||
|
||||
setSelectedDay({
|
||||
date: formattedDate,
|
||||
dayOfWeek: formatDayOfWeek(selectedDate),
|
||||
workdayDetails: workdayDetails.workdayDetails,
|
||||
totalWorkedHours: workdayDetails.totalWorkedHours,
|
||||
absence: null,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Błąd podczas pobierania danych dnia roboczego:", error);
|
||||
setSelectedDay({
|
||||
date: formattedDate,
|
||||
dayOfWeek: formatDayOfWeek(selectedDate),
|
||||
absence: "Błąd pobierania danych",
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setSelectedDay({
|
||||
date: formattedDate,
|
||||
dayOfWeek: formatDayOfWeek(selectedDate),
|
||||
absence: "Brak danych o tym dniu",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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 `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-6">
|
||||
<div className="flex gap-6">
|
||||
<div className="w-2/3">
|
||||
<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 && day.type === 'working' ? 'bg-green-500' : ''}
|
||||
${day && day.type === 'absence' ? 'bg-red-500' : ''}
|
||||
hover:bg-gray-300 transition duration-150 ease-in-out`}
|
||||
onClick={() => handleDayClick(day?.number)}
|
||||
>
|
||||
{day?.number || ''}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-1/3 bg-gray-100 p-4 rounded-lg">
|
||||
{loading ? (
|
||||
<p className="text-center text-blue-500">Ładowanie danych...</p>
|
||||
) : selectedDay ? (
|
||||
<div className="text-center">
|
||||
<h2 className="text-xl font-bold mb-4">
|
||||
Szczegóły dnia {selectedDay.date} ({selectedDay.dayOfWeek})
|
||||
</h2>
|
||||
{selectedDay.absence && selectedDay.absence.trim() !== "" ? (
|
||||
<p>{selectedDay.absence}</p>
|
||||
) : selectedDay.workdayDetails && selectedDay.workdayDetails.length > 0 ? (
|
||||
selectedDay.workdayDetails.map((detail, index) => {
|
||||
const formattedStartTime = new Date(detail.startTime).toLocaleTimeString([], {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
const formattedEndTime = new Date(detail.endTime).toLocaleTimeString([], {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
const workedHours = new Date(`1970-01-01T${detail.workedHours}Z`)
|
||||
.toISOString()
|
||||
.substr(11, 8);
|
||||
|
||||
return (
|
||||
<div key={index} className="mb-2">
|
||||
<p><strong>Start:</strong> {formattedStartTime}</p>
|
||||
<p><strong>Koniec:</strong> {formattedEndTime}</p>
|
||||
<p><strong>Przepracowane godziny:</strong> {workedHours}</p>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<p className="text-gray-500">Brak danych o tym dniu.</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-center text-gray-500">Wybierz dzień, aby zobaczyć szczegóły.</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidokHarmonogramu;
|
||||
|
@ -1,12 +1,11 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import koszIcon from "../icons/kosz.png";
|
||||
import plusIcon from "../icons/plus.png";
|
||||
import ConfirmationModal from './ConfirmationModal';
|
||||
|
||||
const Wydatki = () => {
|
||||
const [expenses, setExpenses] = useState([]);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const [deleteExpenseId, setDeleteExpenseId] = useState(null);
|
||||
const [newExpense, setNewExpense] = useState({
|
||||
@ -23,9 +22,7 @@ const Wydatki = () => {
|
||||
}
|
||||
try {
|
||||
const response = await axios.get('https://localhost:7039/api/Expenses', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
setExpenses(response.data);
|
||||
} catch (error) {
|
||||
@ -42,20 +39,15 @@ const Wydatki = () => {
|
||||
setError('Proszę uzupełnić wszystkie pola.');
|
||||
return;
|
||||
}
|
||||
|
||||
const token = localStorage.getItem('token');
|
||||
try {
|
||||
const response = await axios.post('https://localhost:7039/api/Expenses', newExpense, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
const addedExpense = response.data;
|
||||
setExpenses([...expenses, addedExpense]);
|
||||
setNewExpense({
|
||||
date: '',
|
||||
value: '',
|
||||
description: ''
|
||||
});
|
||||
setNewExpense({ date: '', value: '', description: '' });
|
||||
setShowModal(false);
|
||||
} catch (error) {
|
||||
console.error('Błąd podczas dodawania wydatku:', error);
|
||||
@ -63,16 +55,17 @@ const Wydatki = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteExpense = async (expenseId) => {
|
||||
const handleDeleteExpense = async () => {
|
||||
const token = localStorage.getItem('token');
|
||||
try {
|
||||
await axios.delete(`https://localhost:7039/api/Expenses/${expenseId}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
await axios.delete(`https://localhost:7039/api/Expenses/${deleteExpenseId}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
fetchExpenses();
|
||||
|
||||
// Optimistically update the local state by filtering out the deleted expense
|
||||
setExpenses(expenses.filter(expense => expense.id !== deleteExpenseId));
|
||||
setDeleteExpenseId(null);
|
||||
setShowDeleteModal(false);
|
||||
} catch (error) {
|
||||
console.error('Błąd podczas usuwania wydatku:', error);
|
||||
if (error.response && error.response.data) {
|
||||
@ -85,10 +78,7 @@ const Wydatki = () => {
|
||||
|
||||
const openDeleteConfirmation = (expenseId) => {
|
||||
setDeleteExpenseId(expenseId);
|
||||
};
|
||||
|
||||
const closeDeleteConfirmation = () => {
|
||||
setDeleteExpenseId(null);
|
||||
setShowDeleteModal(true);
|
||||
};
|
||||
|
||||
const formatDate = (dateString) => {
|
||||
@ -100,34 +90,40 @@ const Wydatki = () => {
|
||||
return (
|
||||
<div className="p-10 ml-11">
|
||||
<div className="mt-5">
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='h-20 text-5xl ml-1'>
|
||||
Wydatki
|
||||
</div>
|
||||
<button onClick={() => setShowModal(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
|
||||
<div className="flex items-center justify-between py-6 px-8 bg-gradient-to-r from-blue-500 to-teal-500 rounded-xl shadow-md mb-6">
|
||||
<div className="text-white text-4xl font-semibold">Wydatki</div>
|
||||
<button
|
||||
onClick={() => setShowModal(true)}
|
||||
className="bg-gradient-to-r from-green-500 to-green-700 text-white py-2 px-4 rounded-lg hover:from-green-600 hover:to-green-800 transition"
|
||||
>
|
||||
<span>Dodaj wydatek</span>
|
||||
</button>
|
||||
</div>
|
||||
<table className="w-full border-collapse border border-gray-300">
|
||||
<thead className="bg-gray-200 top-0 z-10">
|
||||
|
||||
<table className="w-full rounded-lg shadow-lg">
|
||||
<thead className="bg-gray-100 text-gray-700">
|
||||
<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">Wartość</th>
|
||||
<th className="border border-gray-300 p-2">Opis</th>
|
||||
<th className="border border-gray-300 p-2"></th>
|
||||
<th className="p-3 text-left">ID</th>
|
||||
<th className="p-3 text-left">Data</th>
|
||||
<th className="p-3 text-left">Wartość</th>
|
||||
<th className="p-3 text-left">Opis</th>
|
||||
<th className="p-3 text-center"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody className="text-gray-600">
|
||||
{expenses.map(expense => (
|
||||
<tr key={expense.id}>
|
||||
<td className="border border-gray-300 p-2">{expense.id}</td>
|
||||
<td className="border border-gray-300 p-2">{formatDate(expense.date)}</td>
|
||||
<td className="border border-gray-300 p-2">{expense.value}</td>
|
||||
<td className="border border-gray-300 p-2">{expense.description}</td>
|
||||
<td className="border border-gray-300 p-2">
|
||||
<button onClick={() => openDeleteConfirmation(expense.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ń
|
||||
<tr key={expense.id} className="hover:bg-gray-50 transition-colors">
|
||||
<td className="p-3">{expense.id}</td>
|
||||
<td className="p-3">{formatDate(expense.date)}</td>
|
||||
<td className="p-3">{expense.value} zł</td>
|
||||
<td className="p-3">{expense.description}</td>
|
||||
<td className="p-3 text-center">
|
||||
<button
|
||||
onClick={() => openDeleteConfirmation(expense.id)}
|
||||
className="bg-gradient-to-r from-red-500 to-red-700 text-white py-2 px-4 rounded-lg hover:from-red-600 hover:to-red-800 transition"
|
||||
>
|
||||
<img src={koszIcon} alt="Usuń" className="inline w-5 mr-2" />
|
||||
<span>Usuń</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@ -135,65 +131,111 @@ const Wydatki = () => {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{showModal && (
|
||||
<div className="fixed z-10 inset-0 overflow-y-auto">
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
<div className="fixed inset-0 transition-opacity">
|
||||
<div className="absolute inset-0 bg-gray-500 opacity-75"></div>
|
||||
</div>
|
||||
<div className="bg-white rounded-lg overflow-hidden shadow-xl transform transition-all sm:max-w-lg sm:w-full">
|
||||
<div className="px-4 py-5 sm:px-6">
|
||||
<h3 className="text-lg font-medium leading-6 text-gray-900">Dodaj nowy wydatek</h3>
|
||||
</div>
|
||||
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<div className="grid grid-cols-6 gap-6">
|
||||
<div className="col-span-6 sm:col-span-3">
|
||||
<label htmlFor="expenseDate" className="block text-sm font-medium text-gray-700">Data</label>
|
||||
<input type="datetime-local" id="expenseDate" value={newExpense.date} onChange={(e) => setNewExpense({ ...newExpense, date: e.target.value })} className="mt-1 border py-1 px-3 block w-full shadow-sm sm:text-sm rounded-md" />
|
||||
</div>
|
||||
<div className="fixed inset-0 bg-gray-500 opacity-50"></div>
|
||||
<div className="bg-gradient-to-r from-blue-500 to-purple-600 p-1 rounded-lg shadow-xl transform transition-all sm:max-w-lg sm:w-full">
|
||||
<div className="bg-white rounded-lg shadow-lg overflow-hidden">
|
||||
<div className="px-4 py-5 sm:px-6 bg-gradient-to-r from-indigo-500 to-indigo-700 text-white">
|
||||
<h3 className="text-lg font-medium">Dodaj nowy wydatek</h3>
|
||||
</div>
|
||||
<div className="bg-white px-4 py-5 sm:p-6">
|
||||
<div className="grid grid-cols-6 gap-6">
|
||||
<div className="col-span-6 sm:col-span-3">
|
||||
<label htmlFor="expenseDate" className="block text-sm font-medium text-gray-700">Data</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
id="expenseDate"
|
||||
value={newExpense.date}
|
||||
onChange={(e) => setNewExpense({ ...newExpense, date: e.target.value })}
|
||||
className="mt-1 py-2 px-3 block w-full shadow-md sm:text-sm rounded-lg border-gray-300 focus:ring-indigo-500 focus:border-indigo-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-span-6 sm:col-span-3">
|
||||
<label htmlFor="expenseValue" className="block text-sm font-medium text-gray-700">Wartość</label>
|
||||
<input type="number" id="expenseValue" value={newExpense.value} onChange={(e) => setNewExpense({ ...newExpense, value: e.target.value })} className="mt-1 border py-1 px-3 block w-full shadow-sm sm:text-sm rounded-md" />
|
||||
</div>
|
||||
<div className="col-span-6 sm:col-span-3">
|
||||
<label htmlFor="expenseValue" className="block text-sm font-medium text-gray-700">Wartość</label>
|
||||
<input
|
||||
type="number"
|
||||
id="expenseValue"
|
||||
value={newExpense.value}
|
||||
onChange={(e) => setNewExpense({ ...newExpense, value: e.target.value })}
|
||||
className="mt-1 py-2 px-3 block w-full shadow-md sm:text-sm rounded-lg border-gray-300 focus:ring-indigo-500 focus:border-indigo-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-span-6">
|
||||
<label htmlFor="expenseDescription" className="block text-sm font-medium text-gray-700">Opis</label>
|
||||
<textarea id="expenseDescription" value={newExpense.description} onChange={(e) => setNewExpense({ ...newExpense, description: e.target.value })}
|
||||
className="mt-1 border py-2 px-3 block w-full shadow-sm sm:text-sm rounded-md" rows="4"/>
|
||||
<div className="col-span-6">
|
||||
<label htmlFor="expenseDescription" className="block text-sm font-medium text-gray-700">Opis</label>
|
||||
<textarea
|
||||
id="expenseDescription"
|
||||
value={newExpense.description}
|
||||
onChange={(e) => setNewExpense({ ...newExpense, description: e.target.value })}
|
||||
className="mt-1 py-2 px-3 block w-full shadow-md sm:text-sm rounded-lg border-gray-300 focus:ring-indigo-500 focus:border-indigo-500"
|
||||
rows="4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||
<button onClick={() => { handleAddExpense(); }} type="button" className="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-600 text-base font-medium text-white hover:bg-green-500 focus:outline-none focus:border-green-700 focus:shadow-outline-green transition ease-in-out duration-150 sm:text-sm sm:leading-5">
|
||||
Dodaj
|
||||
</button>
|
||||
<button onClick={() => setShowModal(false)} type="button" className="mt-3 sm:mt-0 mr-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue transition ease-in-out duration-150 sm:text-sm sm:leading-5">
|
||||
Anuluj
|
||||
</button>
|
||||
<div className="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
onClick={handleAddExpense}
|
||||
type="button"
|
||||
className="w-full inline-flex justify-center rounded-md shadow-lg px-4 py-2 bg-gradient-to-r from-green-400 to-green-600 text-base font-medium text-white hover:bg-green-500 sm:text-sm sm:leading-5"
|
||||
>
|
||||
Dodaj
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowModal(false)}
|
||||
type="button"
|
||||
className="mt-3 sm:mt-0 sm:ml-3 w-full inline-flex justify-center rounded-md shadow-md px-4 py-2 bg-white text-base font-medium text-gray-700 hover:text-gray-500 border-gray-300"
|
||||
>
|
||||
Anuluj
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{showDeleteModal && (
|
||||
<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 wydatek?</h2>
|
||||
<div className="flex justify-between">
|
||||
<button
|
||||
onClick={() => { handleDeleteExpense(); setShowDeleteModal(false); }}
|
||||
className="bg-red-500 text-white py-2 px-4 rounded"
|
||||
>
|
||||
Tak
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowDeleteModal(false)}
|
||||
className="bg-gray-500 text-white py-2 px-4 rounded"
|
||||
>
|
||||
Anuluj
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</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">
|
||||
<button
|
||||
onClick={() => setError(null)}
|
||||
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-lg"
|
||||
>
|
||||
Zamknij
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{deleteExpenseId && (
|
||||
<ConfirmationModal
|
||||
message="Czy na pewno chcesz usunąć ten raport?"
|
||||
onCancel={closeDeleteConfirmation}
|
||||
onConfirm={() => { handleDeleteExpense(deleteExpenseId); }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
Before Width: | Height: | Size: 2.6 KiB |
Loading…
Reference in New Issue
Block a user