Skip to content

Commit

Permalink
exchange rates UI component, reducers, and useExchangeRates.js hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
asimregmi committed Nov 21, 2024
1 parent 9229738 commit 23bb732
Show file tree
Hide file tree
Showing 8 changed files with 384 additions and 1 deletion.
12 changes: 11 additions & 1 deletion src/edit-resource/EditResource.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { SelectInput } from "../shared/SelectInput/SelectInput";
import { ResourceForm } from "./ResourceForm";
import { AllocationGrid } from "./AllocationTypesGrid";
import { AddNewModal } from "./AddNewModal";
import { ExchangeRates } from "./ExchangeRates";
import Alert from "../shared/Alert";
import {
useResourceData,
Expand All @@ -13,7 +14,7 @@ import {
useResourceSubmit,
useAllocationRowsAndColumns,
} from "./helpers/hooks";

import { useExchangeRates } from "./helpers/useExchangeRates";
export default function EditResource({
resourceId,
relativeUrlRoot,
Expand Down Expand Up @@ -50,6 +51,9 @@ export default function EditResource({
handleRequiredResourceChange,
} = useAllocationGrid(resourceData, resourceDetails, dispatch);

const { exchangeRateColumns, exchangeRateRows, handleAddDiscountRate } =
useExchangeRates(resourceData, dispatch);

const handleSubmit = useResourceSubmit(
resourceDetails,
resourceId,
Expand Down Expand Up @@ -164,6 +168,12 @@ export default function EditResource({
onChange={handleSelectNewAllocationType}
/>
</AddNewModal>

<ExchangeRates
columns={exchangeRateColumns}
rows={exchangeRateRows}
onAddDiscountRate={handleAddDiscountRate}
/>
</div>
);
}
Expand Down
35 changes: 35 additions & 0 deletions src/edit-resource/ExchangeRates.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from "react";
import PropTypes from "prop-types";
import Grid from "../shared/Grid";
import style from "./ExchangeRatesGrid.module.scss";

export const ExchangeRates = React.memo(function ExchangeRatesGrid({
columns,
rows,
onAddDiscountRate,
}) {
return (
<div className={style["exchange-rates-grid"]}>
<div className={style["header-container"]}>
<h2 className={style["header-title"]}>Exchange Rates</h2>
<div className={style["header-buttons"]}>
<button className="btn btn-primary" onClick={onAddDiscountRate}>
<i className="fa fa-plus"></i> Add Discount Rate
</button>
</div>
</div>
<Grid
classes={style["no-scroll-grid"]}
columns={columns}
rows={rows}
rowClasses={Array(rows.length).fill(style["vertical-align-center"])}
/>
</div>
);
});

ExchangeRates.propTypes = {
columns: PropTypes.array.isRequired,
rows: PropTypes.array.isRequired,
onAddDiscountRate: PropTypes.func.isRequired,
};
64 changes: 64 additions & 0 deletions src/edit-resource/ExchangeRatesGrid.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
.exchange-rates-grid {
margin: 2rem 0;

.header-container {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}

.header-title {
font-size: 1.25rem;
margin: 0;
}

.header-buttons {
display: flex;
gap: 1rem;

button {
i {
margin-right: 0.5rem;
}
}
}

.vertical-align-center {
td {
vertical-align: middle;
}
}

// Prevent horizontal scrolling when not needed
.no-scroll-grid {
:global(.table) {
margin-bottom: 0;
}

tbody tr:last-child td {
border-bottom: 1px solid #dee2e6;
}
}

// Style inputs within the grid
:global {
.form-control {
margin: 0;
padding: 0.25rem 0.5rem;
}

// Align text inputs and selects
input[type="text"],
input[type="number"],
select {
width: 100%;
min-width: 60px;
}

// Align date inputs
input[type="date"] {
width: 100%;
}
}
}
11 changes: 11 additions & 0 deletions src/edit-resource/helpers/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,17 @@ export const useResourceSubmit = (
},
comment: type.comment || "",
})),
exchange_rates: {
base_rate: resourceDetails.exchange_rates?.base_rate,
discount_rates: resourceDetails.exchange_rates?.discount_rates?.map(
(rate) => ({
id: rate.id,
exchange_rate: rate.exchange_rate,
begin_date: rate.begin_date,
end_date: rate.end_date,
})
),
},
};

const requiredResources = {};
Expand Down
74 changes: 74 additions & 0 deletions src/edit-resource/helpers/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,80 @@ export const resources = (state, action) => {
},
},
};

case "UPDATE_BASE_RATE":
console.log("Reducer: Updating base rate to:", action.payload);
return {
...state,
resourceData: {
...state.resourceData,
resource_details: {
...state.resourceData.resource_details,
exchange_rates: {
...state.resourceData.resource_details.exchange_rates,
base_rate: action.payload,
},
},
},
};

case "UPDATE_EXCHANGE_RATE":
return {
...state,
resourceData: {
...state.resourceData,
resource_details: {
...state.resourceData.resource_details,
exchange_rates: {
...state.resourceData.resource_details.exchange_rates,
discount_rates:
state.resourceData.resource_details.exchange_rates.discount_rates.map(
(rate) =>
rate.id === action.payload.rateId
? { ...rate, ...action.payload.changes }
: rate
),
},
},
},
};

case "ADD_EXCHANGE_RATE":
console.log("Reducer: Adding new discount rate:", action.payload);
return {
...state,
resourceData: {
...state.resourceData,
resource_details: {
...state.resourceData.resource_details,
exchange_rates: {
...state.resourceData.resource_details.exchange_rates,
discount_rates: [
...(state.resourceData.resource_details.exchange_rates
.discount_rates || []),
action.payload,
],
},
},
},
};
case "DELETE_EXCHANGE_RATE":
return {
...state,
resourceData: {
...state.resourceData,
resource_details: {
...state.resourceData.resource_details,
exchange_rates: {
...state.resourceData.resource_details.exchange_rates,
discount_rates:
state.resourceData.resource_details.exchange_rates.discount_rates.filter(
(rate) => rate.id !== action.payload
),
},
},
},
};
default:
return state;
}
Expand Down
156 changes: 156 additions & 0 deletions src/edit-resource/helpers/useExchangeRates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import { useMemo, useCallback } from "react";

export const useExchangeRates = (resourceData, dispatch) => {
const handleDeleteRate = useCallback(
(rateId) => {
dispatch({
type: "DELETE_EXCHANGE_RATE",
payload: rateId,
});
},
[dispatch]
);

const exchangeRateColumns = useMemo(
() => [
{
key: "rate_type",
name: "Rate Type",
width: 150,
},
{
key: "rate",
name: "Rate",
width: 100,
type: "input",
},
{
key: "start_date",
name: "Start Date",
width: 150,
type: "input",
},
{
key: "end_date",
name: "End Date",
width: 150,
type: "input",
},
{
key: "actions",
name: "",
width: 80,
type: "action",
onChange: handleDeleteRate,
},
],
[handleDeleteRate]
);

const handleBaseRateChange = useCallback(
(newValue) => {
dispatch({
type: "UPDATE_BASE_RATE",
payload: newValue,
});
},
[dispatch]
);

const handleRateChange = useCallback(
(rateId, newValue) => {
dispatch({
type: "UPDATE_EXCHANGE_RATE",
payload: {
rateId,
changes: { exchange_rate: newValue },
},
});
},
[dispatch]
);

const handleDateChange = useCallback(
(rateId, dateField, newValue) => {
const fieldMap = {
start_date: "begin_date",
end_date: "end_date",
};

dispatch({
type: "UPDATE_EXCHANGE_RATE",
payload: {
rateId,
changes: { [fieldMap[dateField]]: newValue },
},
});
},
[dispatch]
);

const handleAddDiscountRate = useCallback(() => {
const newRate = {
id: Date.now(),
exchange_rate: "1.0",
begin_date: new Date().toISOString().split("T")[0],
end_date: "",
};

dispatch({
type: "ADD_EXCHANGE_RATE",
payload: newRate,
});
}, [dispatch]);

const exchangeRateRows = useMemo(() => {
const exchangeRates = resourceData?.resource_details?.exchange_rates;
const baseRate = exchangeRates?.base_rate ?? ""; // Default to empty string if no base rate
const discountRates = exchangeRates?.discount_rates || [];

return [
// Base rate row
{
rate_type: "Base Rate",
rate: {
value: baseRate.toString(),
onChange: (newValue) => handleBaseRateChange(newValue),
},
start_date: {
value: "",
disabled: true,
},
end_date: {
value: "",
disabled: true,
},
actions: null,
},
// Discount rate rows
...discountRates.map((rate) => ({
rate_type: "Discount",
rate: {
value: rate.exchange_rate?.toString() || "",
onChange: (newValue) => handleRateChange(rate.id, newValue),
},
start_date: {
value: rate.begin_date || "",
onChange: (newValue) =>
handleDateChange(rate.id, "start_date", newValue),
},
end_date: {
value: rate.end_date || "",
onChange: (newValue) =>
handleDateChange(rate.id, "end_date", newValue),
},
actions: { id: rate.id },
})),
];
}, [resourceData, handleBaseRateChange, handleRateChange, handleDateChange]);

return {
exchangeRateColumns,
exchangeRateRows,
handleAddDiscountRate,
handleDeleteRate,
};
};
Loading

0 comments on commit 23bb732

Please sign in to comment.