Skip to content

Commit

Permalink
merging ramps-583-implement-relative-order-column-in-allocations_proc…
Browse files Browse the repository at this point in the history
…ess_resources to ramps-100-variable-marketplace
  • Loading branch information
asimregmi committed Jan 10, 2025
2 parents 3354bec + 23bb732 commit e9b65c5
Show file tree
Hide file tree
Showing 24 changed files with 3,863 additions and 7,886 deletions.
7,505 changes: 2,021 additions & 5,484 deletions package-lock.json

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions src/edit-resource/AddNewModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import PropTypes from "prop-types";

export const AddNewModal = ({ show, onClose, title, children, onSave }) => {
if (!show) {
return null;
}

return (
<div
style={{
position: "fixed",
top: 0,
left: 0,
width: "100%",
height: "100%",
backgroundColor: "rgba(0, 0, 0, 0.8)",
display: "flex",
alignItems: "center",
zIndex: 1050,
}}
>
<div
className="modal fade in"
tabIndex="-1"
role="dialog"
aria-hidden="true"
style={{
width: "90%",
maxWidth: "600px",
maxHeight: "90%",
overflow: "auto",
backgroundColor: "white",
position: "relative",
}}
>
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<button
type="button"
className="close"
data-dismiss="modal"
onClick={onClose}
aria-label="Close"
>
<h3 aria-hidden="true" className="text-danger">
&times;
</h3>
</button>
<h3 className="modal-title">{title}</h3>
</div>
<div className="modal-body">{children}</div>
<div
className="modal-footer"
style={{
padding: "15px",
textAlign: "right",
borderTop: "1px solid #e5e5e5",
backgroundColor: "#f8f9fa",
}}
>
<button className="btn btn-success" onClick={onSave}>
Save
</button>
</div>
</div>
</div>
</div>
</div>
);
};

AddNewModal.propTypes = {
show: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
title: PropTypes.string.isRequired,
children: PropTypes.node.isRequired,
onSave: PropTypes.func.isRequired,
};
40 changes: 40 additions & 0 deletions src/edit-resource/AllocationTypesGrid.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from "react";
import PropTypes from "prop-types";
import Grid from "../shared/Grid";
import style from "./AllocationTypesGrid.module.scss";

export const AllocationGrid = React.memo(function AllocationGrid({
columns,
rows,
onAddRequiredResource,
onAddAllocationType,
}) {
return (
<div className={style["allocation-types-grid"]}>
<div className={style["header-container"]}>
<h2 className={style["header-title"]}>Allocation Types</h2>
<div className={style["header-buttons"]}>
<button className="btn btn-primary" onClick={onAddAllocationType}>
<i className="fa fa-plus"></i> Add Allocation Type
</button>
<button className="btn btn-primary" onClick={onAddRequiredResource}>
<i className="fa fa-plus"></i> Add Required Resource
</button>
</div>
</div>
<Grid
classes={style["no-scroll-grid"]}
columns={columns}
rows={rows}
rowClasses={Array(rows.length).fill(style["vertical-align-center"])}
/>
</div>
);
});

AllocationGrid.propTypes = {
columns: PropTypes.array.isRequired,
rows: PropTypes.array.isRequired,
onAddRequiredResource: PropTypes.func.isRequired,
onAddAllocationType: PropTypes.func.isRequired,
};
25 changes: 25 additions & 0 deletions src/edit-resource/AllocationTypesGrid.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.header-container {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}

.header-buttons {
display: flex;
gap: 1rem; /* Space between the buttons */
}

.no-scroll-grid {
overflow: auto; //hidden for no-scroll
height: auto;
}

.allocation-types-grid {
margin-bottom: 0.8rem;
margin-top: 0.8rem;
}

.vertical-align-center td {
vertical-align: middle;
}
185 changes: 185 additions & 0 deletions src/edit-resource/EditResource.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import { useEffect } from "react";
import PropTypes from "prop-types";
import LoadingSpinner from "../shared/LoadingSpinner";
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,
useResourceOptions,
useAllocationGrid,
useResourceSubmit,
useAllocationRowsAndColumns,
} from "./helpers/hooks";
import { useExchangeRates } from "./helpers/useExchangeRates";
export default function EditResource({
resourceId,
relativeUrlRoot,
setExternalSubmit,
}) {
const { state, dispatch, handleError, fetchData } = useResourceData(
resourceId,
relativeUrlRoot
);
const { resourceData, loading, errors, successMessage } = state;
const resourceDetails = resourceData?.resource_details;

const {
allowedActionsOptions,
resourceTypesOptions,
unitTypesOptions,
availableResources,
availableAllocationTypes,
} = useResourceOptions(resourceData);

const {
showAddResourceModal,
setShowAddResourceModal,
showAddAllocationTypeModal,
setShowAddAllocationTypeModal,
selectedNewResource,
handleSelectNewResource,
handleSaveResources,
selectedNewAllocationType,
handleSelectNewAllocationType,
handleSaveAllocationType,
handleAllowedActionChange,
handleCommentChange,
handleRequiredResourceChange,
} = useAllocationGrid(resourceData, resourceDetails, dispatch);

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

const handleSubmit = useResourceSubmit(
resourceDetails,
resourceId,
relativeUrlRoot,
fetchData,
handleError,
dispatch
);

// Expose handleSubmit to external Rails template script
useEffect(() => {
if (setExternalSubmit) {
setExternalSubmit(handleSubmit);
}
}, [handleSubmit, setExternalSubmit]);

const { allocationColumns, allocationRows } = useAllocationRowsAndColumns(
resourceDetails,
availableResources,
selectedNewResource,
allowedActionsOptions,
handleAllowedActionChange,
handleCommentChange,
handleRequiredResourceChange
);

if (loading) return <LoadingSpinner />;
if (errors.length > 0) {
return (
<div>
{errors.map((error, index) => (
<Alert key={index} color="danger">
{error}
</Alert>
))}
</div>
);
}
if (!resourceData) return <div>No resource data available.</div>;

return (
<div className="edit-resource">
{successMessage.message && (
<Alert color={successMessage.color}>{successMessage.message}</Alert>
)}
<div>
<h2>Edit Resource</h2>
<ResourceForm
resourceDetails={resourceDetails}
resourceTypesOptions={resourceTypesOptions}
unitTypesOptions={unitTypesOptions}
dispatch={dispatch}
/>
</div>

<AllocationGrid
columns={allocationColumns}
rows={allocationRows}
onAddRequiredResource={() => setShowAddResourceModal(true)}
onAddAllocationType={() => setShowAddAllocationTypeModal(true)}
/>

<AddNewModal
show={showAddResourceModal}
onClose={() => setShowAddResourceModal(false)}
title="Add Required Resource"
onSave={handleSaveResources}
>
<div>
{availableResources.map((resource) => (
<label
key={resource.resource_id}
style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}
>
<input
type="checkbox"
style={{ margin: 0 }}
checked={selectedNewResource.includes(resource.resource_id)}
onChange={(e) =>
handleSelectNewResource(
resource.resource_id,
e.target.checked
)
}
/>
{resource.resource_name}
</label>
))}
</div>
</AddNewModal>

<AddNewModal
show={showAddAllocationTypeModal}
onClose={() => setShowAddAllocationTypeModal(false)}
title="Add Allocation Type"
onSave={handleSaveAllocationType}
>
<SelectInput
label="Select Allocation Type"
options={[
{
value: "",
label: "Select an allocation type to add",
disabled: true,
},
...availableAllocationTypes.map((at) => ({
value: at.allocation_type_id,
label: at.display_name,
})),
]}
value={selectedNewAllocationType}
onChange={handleSelectNewAllocationType}
/>
</AddNewModal>

<ExchangeRates
columns={exchangeRateColumns}
rows={exchangeRateRows}
onAddDiscountRate={handleAddDiscountRate}
/>
</div>
);
}

EditResource.propTypes = {
resourceId: PropTypes.number.isRequired,
relativeUrlRoot: PropTypes.string.isRequired,
setExternalSubmit: PropTypes.func,
};
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,
};
Loading

0 comments on commit e9b65c5

Please sign in to comment.