poprawy wizualne
This commit is contained in:
parent
44fa0d8503
commit
8e6726348d
@ -190,14 +190,6 @@ const DodawanieTransakcji = () => {
|
||||
|
||||
</div>
|
||||
|
||||
{/*<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 shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>*/}
|
||||
<label className="block mb-2 text-gray-700 font-medium">Produkty transakcji</label>
|
||||
<div className="border border-gray-300 rounded-lg shadow-sm p-4 h-80 overflow-y-scroll">
|
||||
{isLoading ? (
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import {ReactComponent as MinusIcon} from "../icons/minus-icon.svg"
|
||||
import Select from "react-select";
|
||||
|
||||
const EdycjaTransakcji = () => {
|
||||
@ -204,105 +205,148 @@ const EdycjaTransakcji = () => {
|
||||
|
||||
return (
|
||||
<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>
|
||||
<h2 className="text-2xl font-bold mb-6 text-gray-800">Edytuj transakcję</h2>
|
||||
|
||||
{error && <p className="text-red-500 mb-4">{error}</p>}
|
||||
|
||||
<div className="mb-4">
|
||||
<div className="mb-4 flex items-center space-x-4">
|
||||
<div>
|
||||
<label className="block mb-2 text-gray-700 font-medium">Data transakcji</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
name="date"
|
||||
value={transaction.date}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Data"
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
className="flex-1 mb-4 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">
|
||||
</div>
|
||||
<div className="pb-4 items-center">
|
||||
<label className="block mb-2 text-gray-700 font-medium">Nr pracownika</label>
|
||||
<input
|
||||
type="number"
|
||||
name="employeeId"
|
||||
value={transaction.employeeId}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Nr. Pracownika"
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
placeholder="Nr pracownika"
|
||||
className="flex mb-4 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>
|
||||
|
||||
<label className="block mb-2 text-gray-700 font-medium">Produkty transkacji</label>
|
||||
<div className="border border-gray-300 rounded-lg shadow-sm p-4 h-80 overflow-y-scroll">
|
||||
{transaction.transactionProducts.map((product, index) => (
|
||||
<div key={index} className="mb-4">
|
||||
<div key={index} className="mb-4 flex items-center space-x-4">
|
||||
<Select
|
||||
value={products.find((p) => p.value === product.productID) || null}
|
||||
onChange={(option) => handleProductChange(index, option)}
|
||||
name={`productName-${index}`}
|
||||
value={products.find((option) => option.value === product.productID)}
|
||||
onChange={(selectedOption) => handleProductChange(index, selectedOption)}
|
||||
options={products}
|
||||
className="mb-2"
|
||||
placeholder="Wybierz produkt"
|
||||
className="flex-1"
|
||||
placeholder="Wybierz produkt..."
|
||||
/>
|
||||
{errors[`productID_${index}`] && (
|
||||
<span className="text-red-500 text-sm">{errors[`productID_${index}`]}</span>
|
||||
)}
|
||||
<input
|
||||
type="number"
|
||||
name={`quantity-${index}`}
|
||||
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 shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
className="w-24 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-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"
|
||||
className="relative flex items-center justify-center w-10 h-10 rounded-full text-gray-500 hover:text-red-600 hover:bg-red-100 active:bg-red-200 transition focus:outline-none"
|
||||
>
|
||||
Usuń
|
||||
<MinusIcon className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<button
|
||||
onClick={handleAddProduct}
|
||||
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"
|
||||
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>
|
||||
|
||||
<div className="mb-4">
|
||||
<input
|
||||
type="text"
|
||||
name="paymentType"
|
||||
value={transaction.paymentType}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Sposób płatności"
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
{errors.paymentType && <span className="text-red-500 text-sm">{errors.paymentType}</span>}
|
||||
</div>
|
||||
|
||||
<div className="mt-6 flex justify-between">
|
||||
<div className="mb-4">
|
||||
<label className="block mb-2 text-gray-700 font-medium">Metoda płatności</label>
|
||||
<div className="flex space-x-4">
|
||||
<label className="flex items-center">
|
||||
<input
|
||||
type="radio"
|
||||
name="paymentType"
|
||||
value="BLIK"
|
||||
checked={transaction.paymentType === "BLIK"}
|
||||
onChange={handleInputChange}
|
||||
className="form-radio h-5 w-5 text-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
<span className="ml-2">BLIK</span>
|
||||
</label>
|
||||
<label className="flex items-center">
|
||||
<input
|
||||
type="radio"
|
||||
name="paymentType"
|
||||
value="Gotówka"
|
||||
checked={transaction.paymentType === "Gotówka"}
|
||||
onChange={handleInputChange}
|
||||
className="form-radio h-5 w-5 text-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
<span className="ml-2">Gotówka</span>
|
||||
</label>
|
||||
<label className="flex items-center">
|
||||
<input
|
||||
type="radio"
|
||||
name="paymentType"
|
||||
value="Karta płatnicza"
|
||||
checked={transaction.paymentType === "Karta płatnicza"}
|
||||
onChange={handleInputChange}
|
||||
className="form-radio h-5 w-5 text-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
<span className="ml-2">Karta płatnicza</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block mb-2 text-gray-700 font-medium">Rabat</label>
|
||||
<input
|
||||
type="number"
|
||||
name="discount"
|
||||
value={transaction.discount}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Rabat"
|
||||
className="block w-full px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
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"
|
||||
/>
|
||||
{errors.discount && <span className="text-red-500 text-sm">{errors.discount}</span>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block mb-2 text-gray-700 font-medium">Opis</label>
|
||||
<input
|
||||
type="text"
|
||||
name="description"
|
||||
value={transaction.description}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Opis"
|
||||
className="block w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<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"
|
||||
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"
|
||||
>
|
||||
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"
|
||||
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>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import editIcon from '../icons/edit.png';
|
||||
import koszIcon from '../icons/kosz.png';
|
||||
import {ReactComponent as EditIcon} from '../icons/edit.svg';
|
||||
import {ReactComponent as KoszIcon} from '../icons/delete.svg';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const ListaProduktow = ({ onAdd }) => {
|
||||
@ -26,6 +26,11 @@ const ListaProduktow = ({ onAdd }) => {
|
||||
fetchProducts();
|
||||
}, []);
|
||||
|
||||
const openDeleteConfirmation = (productId) => {
|
||||
setDeleteProductId(productId);
|
||||
setShowModal(true);
|
||||
};
|
||||
|
||||
const handleDeleteProduct = async () => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) {
|
||||
@ -76,7 +81,7 @@ const ListaProduktow = ({ onAdd }) => {
|
||||
</thead>
|
||||
<tbody className="text-gray-600">
|
||||
{products.map((product) => (
|
||||
<tr key={product.id} className="hover:bg-gray-50 transition-colors">
|
||||
<tr key={product.id} className="group hover:bg-gray-100 transition-colors">
|
||||
<td className="p-3">{product.id}</td>
|
||||
<td className="p-3">{product.name}</td>
|
||||
<td className="p-3">{product.description}</td>
|
||||
@ -84,25 +89,20 @@ const ListaProduktow = ({ onAdd }) => {
|
||||
<td className="p-3 text-center">
|
||||
{product.type === 0 ? "" : product.availability}
|
||||
</td>
|
||||
<td className="p-3 flex justify-center items-center space-x-2">
|
||||
<div className="flex justify-center items-center opacity-0 group-hover:opacity-100 transition-opacity duration-200">
|
||||
<button
|
||||
onClick={() => handleEditProduct(product.id)}
|
||||
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"
|
||||
className="text-blue-500 hover:bg-blue-200 active:bg-blue-300 focus:outline-none p-2 rounded-full transition-colors"
|
||||
>
|
||||
<img src={editIcon} alt="Edytuj" className="inline w-5 mr-2" />
|
||||
Edytuj
|
||||
<EditIcon className = "w-5 h-5"/>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setDeleteProductId(product.id);
|
||||
setShowModal(true);
|
||||
}}
|
||||
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"
|
||||
onClick={() => openDeleteConfirmation(product.id)}
|
||||
className="text-red-500 hover:bg-red-200 active:bg-red-300 focus:outline-none p-2 rounded-full transition-colors"
|
||||
>
|
||||
<img src={koszIcon} alt="Usuń" className="inline w-5 mr-2" />
|
||||
Usuń
|
||||
<KoszIcon className = "w-5 h-5"/>
|
||||
</button>
|
||||
</td>
|
||||
</div>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
@ -1,7 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
//import editIcon from '../icons/edit.png';
|
||||
//import koszIcon from '../icons/kosz.png';
|
||||
import {ReactComponent as EditIcon} from '../icons/edit.svg';
|
||||
import {ReactComponent as KoszIcon} from '../icons/delete.svg';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
@ -133,14 +131,12 @@ const ListaTransakcji = ({ onAdd}) => {
|
||||
<div className="flex space-x-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
|
||||
<button
|
||||
onClick={() => handleEditTransaction(transaction.id)}
|
||||
//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"
|
||||
className="text-blue-500 hover:bg-blue-200 active:bg-blue-300 focus:outline-none p-2 rounded-full transition-colors"
|
||||
>
|
||||
<EditIcon className = "w-5 h-5"/>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => openDeleteConfirmation(transaction.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"
|
||||
className="text-red-500 hover:bg-red-200 active:bg-red-300 focus:outline-none p-2 rounded-full transition-colors"
|
||||
>
|
||||
<KoszIcon className = "w-5 h-5"/>
|
||||
|
@ -12,7 +12,6 @@ const PanelAdministratora = () => {
|
||||
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', {
|
||||
@ -24,7 +23,6 @@ const PanelAdministratora = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Funkcja dodawania absencji
|
||||
const addAbsence = async () => {
|
||||
if (!selectedEmail || !absenceType || !startDate || !endDate) {
|
||||
alert("Wszystkie pola muszą być wypełnione!");
|
||||
@ -47,7 +45,6 @@ const PanelAdministratora = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Funkcja pobierania raportu
|
||||
const downloadReport = async () => {
|
||||
if (!reportType || !startDate || !endDate) {
|
||||
alert("Wszystkie pola muszą być wypełnione!");
|
||||
@ -76,7 +73,6 @@ const PanelAdministratora = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Funkcja pobierania harmonogramów
|
||||
const fetchWorkdays = async (userEmail) => {
|
||||
if (!userEmail) {
|
||||
setWorkdays([]);
|
||||
@ -93,7 +89,6 @@ const PanelAdministratora = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// UseEffect do pobierania danych emaili przy pierwszym renderowaniu
|
||||
useEffect(() => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
@ -101,7 +96,6 @@ const PanelAdministratora = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
// UseEffect do pobierania harmonogramu przy zmianie emaila
|
||||
useEffect(() => {
|
||||
if (selectedEmail) {
|
||||
fetchWorkdays(selectedEmail);
|
||||
@ -113,7 +107,7 @@ const PanelAdministratora = () => {
|
||||
<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'>
|
||||
<div className='px-5'>
|
||||
<button
|
||||
onClick={() => setSelectedOption('harmonogramy')}
|
||||
className={`
|
||||
@ -123,7 +117,7 @@ const PanelAdministratora = () => {
|
||||
Harmonogramy
|
||||
</button>
|
||||
</div>
|
||||
<div className='px-10'>
|
||||
<div className='px-5'>
|
||||
<button
|
||||
onClick={() => setSelectedOption('absencje')}
|
||||
className={`
|
||||
@ -133,7 +127,7 @@ const PanelAdministratora = () => {
|
||||
Absencje
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<div className='px-5'>
|
||||
<button
|
||||
onClick={() => setSelectedOption('raporty')}
|
||||
className={`
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import koszIcon from "../icons/kosz.png";
|
||||
import {ReactComponent as KoszIcon} from '../icons/delete.svg';
|
||||
|
||||
const Raporty = () => {
|
||||
const [fromDate, setFromDate] = useState('');
|
||||
@ -28,6 +28,31 @@ const Raporty = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const validateYear = (dateString) => {
|
||||
const year = dateString.split('-')[0];
|
||||
return year.length === 4 && /^\d{4}$/.test(year);
|
||||
};
|
||||
|
||||
const handleFromDateChange = (e) => {
|
||||
const value = e.target.value;
|
||||
if (validateYear(value)) {
|
||||
setFromDate(value);
|
||||
setError(null);
|
||||
} else {
|
||||
setError('Rok w dacie "Od" musi być 4-cyfrowy.');
|
||||
}
|
||||
};
|
||||
|
||||
const handleToDateChange = (e) => {
|
||||
const value = e.target.value;
|
||||
if (validateYear(value)) {
|
||||
setToDate(value);
|
||||
setError(null);
|
||||
} else {
|
||||
setError('Rok w dacie "Do" musi być 4-cyfrowy.');
|
||||
}
|
||||
};
|
||||
|
||||
const openDeleteConfirmation = (reportId) => {
|
||||
setDeleteReportId(reportId);
|
||||
setShowDeleteModal(true);
|
||||
@ -110,7 +135,7 @@ const Raporty = () => {
|
||||
type="datetime-local"
|
||||
id="fromDate"
|
||||
value={fromDate}
|
||||
onChange={(e) => setFromDate(e.target.value)}
|
||||
onChange={handleFromDateChange}
|
||||
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>
|
||||
@ -121,7 +146,7 @@ const Raporty = () => {
|
||||
type="datetime-local"
|
||||
id="toDate"
|
||||
value={toDate}
|
||||
onChange={(e) => setToDate(e.target.value)}
|
||||
onChange={handleToDateChange}
|
||||
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>
|
||||
@ -133,7 +158,14 @@ const Raporty = () => {
|
||||
>
|
||||
Generuj raport
|
||||
</button>
|
||||
|
||||
{error && (
|
||||
<div className="mt-4 text-red-500 text-center font-semibold">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -147,25 +179,27 @@ const Raporty = () => {
|
||||
<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>
|
||||
<th className="p-2 text-center">Usuń</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{reports.map((report) => (
|
||||
<tr key={report.id} className="hover:bg-gray-50">
|
||||
<tr key={report.id} className="group hover:bg-gray-100 transition-colors">
|
||||
<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">
|
||||
<td className="p-3 flex justify-center space-x-2">
|
||||
<div className="flex space-x-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
|
||||
<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"
|
||||
className="text-red-500 hover:bg-red-200 active:bg-red-300 focus:outline-none p-2 rounded-full transition-colors"
|
||||
>
|
||||
<img src={koszIcon} alt="Usuń" className="inline w-5 mr-2" /> Usuń
|
||||
<KoszIcon className = "w-5 h-5"/>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
@ -173,21 +207,6 @@ const Raporty = () => {
|
||||
</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-lg"
|
||||
>
|
||||
Zamknij
|
||||
</button>
|
||||
</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">
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import koszIcon from "../icons/kosz.png";
|
||||
import { ReactComponent as KoszIcon } from '../icons/delete.svg';
|
||||
|
||||
const Wydatki = () => {
|
||||
const [expenses, setExpenses] = useState([]);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [fromDate, setFromDate] = useState('');
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const [deleteExpenseId, setDeleteExpenseId] = useState(null);
|
||||
@ -40,6 +40,11 @@ const Wydatki = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newExpense.value <= 0) {
|
||||
setError('Wartość wydatku musi być liczbą dodatnią.');
|
||||
return;
|
||||
}
|
||||
|
||||
const token = localStorage.getItem('token');
|
||||
try {
|
||||
const response = await axios.post('https://localhost:7039/api/Expenses', newExpense, {
|
||||
@ -48,7 +53,6 @@ const Wydatki = () => {
|
||||
const addedExpense = response.data;
|
||||
setExpenses([...expenses, addedExpense]);
|
||||
setNewExpense({ date: '', value: '', description: '' });
|
||||
setShowModal(false);
|
||||
} catch (error) {
|
||||
console.error('Błąd podczas dodawania wydatku:', error);
|
||||
setError('Wystąpił błąd podczas dodawania wydatku.');
|
||||
@ -62,7 +66,6 @@ const Wydatki = () => {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
// Optimistically update the local state by filtering out the deleted expense
|
||||
setExpenses(expenses.filter(expense => expense.id !== deleteExpenseId));
|
||||
setDeleteExpenseId(null);
|
||||
setShowDeleteModal(false);
|
||||
@ -87,19 +90,80 @@ const Wydatki = () => {
|
||||
return date.toLocaleDateString('pl-PL', options).replace(",", "");
|
||||
};
|
||||
|
||||
const handleDateChange = (e) => {
|
||||
setNewExpense({ ...newExpense, date: e.target.value });
|
||||
};
|
||||
|
||||
const handleValueChange = (e) => {
|
||||
setNewExpense({ ...newExpense, value: e.target.value });
|
||||
};
|
||||
|
||||
const handleDescriptionChange = (e) => {
|
||||
setNewExpense({ ...newExpense, description: e.target.value });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-10 ml-11">
|
||||
<div className="mt-5">
|
||||
<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>
|
||||
<h1 className="text-white text-4xl font-semibold">Wydatki</h1>
|
||||
</div>
|
||||
|
||||
<div className="bg-white shadow-lg p-8 rounded-xl max-w-3xl mx-auto">
|
||||
<div className="mb-6">
|
||||
<div className="flex flex-col space-y-6">
|
||||
<div className="flex space-x-6">
|
||||
<div className="flex-1">
|
||||
<label htmlFor="expenseDate" className="block text-sm font-medium text-gray-700">Data</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
id="expenseDate"
|
||||
value={newExpense.date}
|
||||
onChange={handleDateChange}
|
||||
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="flex-1">
|
||||
<label htmlFor="expenseValue" className="block text-sm font-medium text-gray-700">Wartość</label>
|
||||
<input
|
||||
type="number"
|
||||
id="expenseValue"
|
||||
value={newExpense.value}
|
||||
onChange={handleValueChange}
|
||||
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>
|
||||
|
||||
<div>
|
||||
<label htmlFor="expenseDescription" className="block text-sm font-medium text-gray-700">Opis</label>
|
||||
<textarea
|
||||
id="expenseDescription"
|
||||
value={newExpense.description}
|
||||
onChange={handleDescriptionChange}
|
||||
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>
|
||||
|
||||
<button
|
||||
onClick={handleAddExpense}
|
||||
type="button"
|
||||
className="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 w-full"
|
||||
>
|
||||
Dodaj
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="mt-4 text-red-500 text-center font-semibold">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-5">
|
||||
<table className="w-full rounded-lg shadow-lg">
|
||||
<thead className="bg-gray-100 text-gray-700">
|
||||
<tr>
|
||||
@ -107,24 +171,25 @@ const Wydatki = () => {
|
||||
<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>
|
||||
<th className="p-3 text-center">Usuń</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-gray-600">
|
||||
{expenses.map(expense => (
|
||||
<tr key={expense.id} className="hover:bg-gray-50 transition-colors">
|
||||
<tr key={expense.id} className="group hover:bg-gray-100 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">
|
||||
<td className="p-3 flex justify-center space-x-2">
|
||||
<div className="flex space-x-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
|
||||
<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"
|
||||
className="text-red-500 hover:bg-red-200 active:bg-red-300 focus:outline-none p-2 rounded-full transition-colors"
|
||||
>
|
||||
<img src={koszIcon} alt="Usuń" className="inline w-5 mr-2" />
|
||||
<span>Usuń</span>
|
||||
<KoszIcon className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
@ -132,74 +197,6 @@ const Wydatki = () => {
|
||||
</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 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 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 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 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">
|
||||
@ -221,21 +218,6 @@ const Wydatki = () => {
|
||||
</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={() => setError(null)}
|
||||
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-lg"
|
||||
>
|
||||
Zamknij
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user