skończone harmonogramy oraz poprawy wizualne

This commit is contained in:
Wiktor Szynaka 2024-12-26 11:14:44 +01:00
parent 4659ef4b34
commit b327d3deda
13 changed files with 1077 additions and 782 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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>
);
};

View File

@ -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)} </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">

View File

@ -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">

View File

@ -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;

View File

@ -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>
);

View File

@ -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;

View File

@ -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} </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>
);
};

View File

@ -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