|
@@ -56,6 +77,7 @@ const ItemRow = (props) => {
value: props.item.itemName,
id: props.item.itemId,
}}
+ options={props.allItems}
/>
@@ -87,16 +109,14 @@ const ItemRow = (props) => {
|
- props.onItemizedItemEdit(evt, props.item.itemId)
- }
+ onItemizedItemEdit={handlePriceChange}
cellData={{
leading: props.currency,
type: "number",
name: "itemPrice",
min: 1,
step: "0.01",
- presicion: 2,
+ precision: 2,
textAlign: "text-end",
value: props.item.itemPrice,
id: props.item.itemId,
diff --git a/src/components/ProductsTab.jsx b/src/components/ProductsTab.jsx
new file mode 100644
index 0000000..e207028
--- /dev/null
+++ b/src/components/ProductsTab.jsx
@@ -0,0 +1,141 @@
+import React, { useState } from "react";
+import { Button, Card, Col, Row, Table } from "react-bootstrap";
+import { BiTrash } from "react-icons/bi";
+import { BsEyeFill } from "react-icons/bs";
+import InvoiceModal from "../components/InvoiceModal";
+import { useNavigate } from "react-router-dom";
+import { useInvoiceListData } from "../redux/hooks";
+import { useDispatch } from "react-redux";
+import { addItemToForm, deleteItem } from "../redux/invoicesSlice";
+
+const ProductsTab = () => {
+ const { invoiceList } = useInvoiceListData();
+ const isListEmpty = invoiceList.length === 0;
+ const navigate = useNavigate();
+
+ const allItems = invoiceList.flatMap(invoice =>
+ invoice.items.map(item => ({
+ ...item,
+ invoiceNumber: invoice.invoiceNumber,
+ currency: invoice.currency,
+ invoiceId: invoice.id,
+ }))
+ );
+
+ return (
+
+
+
+ {isListEmpty ? (
+
+ No invoices present
+
+ ) : (
+
+
+ Items List
+
+
+
+
+ | Invoice No. |
+ Item Name |
+ Item Price |
+ Actions |
+
+
+
+ {allItems.map((item) => (
+
+ ))}
+
+
+
+ )}
+
+
+
+ );
+};
+
+const ItemRow = ({ item, navigate }) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const dispatch = useDispatch();
+
+ const handleDeleteClick = () => {
+ dispatch(deleteItem({ invoiceId: item.invoiceId, itemId: item.itemId }));
+ };
+
+
+ const openModal = (event) => {
+ event.preventDefault();
+ setIsOpen(true);
+ };
+
+ const closeModal = () => {
+ setIsOpen(false);
+ };
+
+ return (
+
+ | {item.invoiceNumber} |
+ {item.itemName} |
+
+ {item.currency}
+ {item.itemPrice}
+ |
+
+
+
+ |
+
+
+ |
+
+
+ );
+};
+
+export default ProductsTab;
diff --git a/src/redux/currencySlice.js b/src/redux/currencySlice.js
new file mode 100644
index 0000000..80567bd
--- /dev/null
+++ b/src/redux/currencySlice.js
@@ -0,0 +1,32 @@
+import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
+import { fetchExchangeRates } from '../utils/CurrencyUtil';
+
+export const fetchRates = createAsyncThunk('currency/fetchRates', async () => {
+ const rates = await fetchExchangeRates();
+ return rates;
+});
+
+const currencySlice = createSlice({
+ name: 'currency',
+ initialState: {
+ rates: {},
+ selectedCurrency: 'USD',
+ },
+ reducers: {
+ setCurrency: (state, action) => {
+ state.selectedCurrency = action.payload;
+ },
+ },
+ extraReducers: (builder) => {
+ builder.addCase(fetchRates.fulfilled, (state, action) => {
+ state.rates = action.payload;
+ });
+ },
+});
+
+export const { setCurrency } = currencySlice.actions;
+
+export const selectRates = (state) => state.currency.rates;
+export const selectSelectedCurrency = (state) => state.currency.selectedCurrency;
+
+export default currencySlice.reducer;
diff --git a/src/redux/index.js b/src/redux/index.js
index 0144da7..ad45f47 100644
--- a/src/redux/index.js
+++ b/src/redux/index.js
@@ -1,8 +1,12 @@
import { combineReducers } from "@reduxjs/toolkit";
-import invoicesReducer from "./invoicesSlice"; // Import your other reducers
+import invoicesReducer from "./invoicesSlice";
+import productsReducer from "./productsSlice";
+import currencyReducer from './currencySlice';
const rootReducer = combineReducers({
invoices: invoicesReducer,
+ products: productsReducer,
+ currency: currencyReducer,
});
export default rootReducer;
diff --git a/src/redux/invoicesSlice.js b/src/redux/invoicesSlice.js
index 26b6d48..703ed65 100644
--- a/src/redux/invoicesSlice.js
+++ b/src/redux/invoicesSlice.js
@@ -10,14 +10,38 @@ const invoicesSlice = createSlice({
deleteInvoice: (state, action) => {
return state.filter((invoice) => invoice.id !== action.payload);
},
+ deleteItem(state, action) {
+ const { invoiceId, itemId } = action.payload;
+ const invoice = state.find(invoice => invoice.id === invoiceId);
+ if (invoice) {
+ invoice.items = invoice.items.filter(item => item.itemId !== itemId);
+ }
+ },
updateInvoice: (state, action) => {
+ console.log("updateInvoice reducer called with:", action.payload);
+ console.log(action.payload.id);
+
+ const idToUpdate = Number(action.payload.id);
const index = state.findIndex(
- (invoice) => invoice.id === action.payload.id
+ (invoice) => invoice.id === idToUpdate
);
+ console.log(index);
+
if (index !== -1) {
- state[index] = action.payload.updatedInvoice;
+ state[index] = {
+ ...state[index],
+ ...action.payload.updatedInvoice,
+ };
+ console.log("Updated invoice:", state[index]);
}
},
+ addItemToForm: (state, action) => {
+ const { invoiceId, newItem } = action.payload;
+ const invoice = state.find(invoice => invoice.id === invoiceId);
+ if (invoice) {
+ invoice.items.push(newItem);
+ }
+ }
},
});
@@ -25,6 +49,8 @@ export const {
addInvoice,
deleteInvoice,
updateInvoice,
+ deleteItem,
+ addItemToForm
} = invoicesSlice.actions;
export const selectInvoiceList = (state) => state.invoices;
diff --git a/src/redux/productsSlice.js b/src/redux/productsSlice.js
new file mode 100644
index 0000000..b84e138
--- /dev/null
+++ b/src/redux/productsSlice.js
@@ -0,0 +1,24 @@
+
+import { createSlice } from '@reduxjs/toolkit';
+
+const productsSlice = createSlice({
+ name: 'products',
+ initialState: [],
+ reducers: {
+ addProduct: (state, action) => {
+ state.push(action.payload);
+ },
+ updateProduct: (state, action) => {
+ const index = state.findIndex(product => product.id === action.payload.id);
+ if (index !== -1) {
+ state[index] = action.payload;
+ }
+ },
+ },
+});
+
+export const { addProduct, updateProduct } = productsSlice.actions;
+
+export const selectProductList = state => state.products;
+
+export default productsSlice.reducer;
diff --git a/src/utils/CurrencyUtil.js b/src/utils/CurrencyUtil.js
new file mode 100644
index 0000000..8e026d4
--- /dev/null
+++ b/src/utils/CurrencyUtil.js
@@ -0,0 +1,13 @@
+import axios from 'axios';
+
+const API_KEY = 'fca_live_2yq1EhohyoNXfpbStUViNWSSAlBiUdlBCQC6BKPO';
+
+export const fetchExchangeRates = async () => {
+ try {
+ const response = await axios.get(`https://api.freecurrencyapi.com/v1/latest?apikey=${API_KEY}`);
+ return response.data.data;
+ } catch (error) {
+ console.error("Error fetching exchange rates:", error);
+ return null;
+ }
+};
|