mirror of https://github.com/ANL-CEEESA/RELOG.git
parent
92d30460b9
commit
0e53a4334e
@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
@ -0,0 +1,70 @@
|
||||
# Getting Started with Create React App
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm start`
|
||||
|
||||
Runs the app in the development mode.\
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
||||
|
||||
The page will reload when you make changes.\
|
||||
You may also see any lint errors in the console.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.\
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `build` folder.\
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.\
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `npm run eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
|
||||
|
||||
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
|
||||
|
||||
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
|
||||
### Code Splitting
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
|
||||
|
||||
### Analyzing the Bundle Size
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
|
||||
|
||||
### Making a Progressive Web App
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
|
||||
|
||||
### Deployment
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
|
||||
|
||||
### `npm run build` fails to minify
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "relog-web",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.16.2",
|
||||
"@testing-library/react": "^12.1.4",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-flow-renderer": "^9.7.4",
|
||||
"react-scripts": "5.0.0",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>RELOG</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,22 @@
|
||||
import styles from './Button.module.css'
|
||||
|
||||
const Button = (props) => {
|
||||
let className = styles.Button
|
||||
if (props.kind === "inline") {
|
||||
className += " " + styles.inline
|
||||
}
|
||||
|
||||
let tooltip = "";
|
||||
if (props.tooltip != undefined) {
|
||||
tooltip = <span className={styles.tooltip}>{props.tooltip}</span>
|
||||
}
|
||||
|
||||
return (
|
||||
<button className={className}>
|
||||
{tooltip}
|
||||
{props.label}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
export default Button;
|
@ -0,0 +1,65 @@
|
||||
.Button {
|
||||
padding: 6px 36px;
|
||||
margin: 12px 6px;
|
||||
line-height: 24px;
|
||||
border: var(--box-border);
|
||||
/* background-color: white; */
|
||||
box-shadow: var(--box-shadow);
|
||||
border-radius: var(--border-radius);
|
||||
cursor: pointer;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
background: linear-gradient(
|
||||
rgb(255, 255, 255) 25%,
|
||||
rgb(245, 245, 245) 100%
|
||||
)
|
||||
}
|
||||
|
||||
.Button:hover {
|
||||
background: rgb(245, 245, 245);
|
||||
}
|
||||
|
||||
.Button:active {
|
||||
background: rgba(220, 220, 220);
|
||||
}
|
||||
|
||||
.inline {
|
||||
padding: 0 12px;
|
||||
margin: 2px 4px 2px 0;
|
||||
height: 32px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* .inline:last-child {
|
||||
margin: 2px 1px;
|
||||
} */
|
||||
|
||||
.tooltip {
|
||||
visibility: hidden;
|
||||
background-color: #333;
|
||||
color: white;
|
||||
opacity: 0%;
|
||||
width: 180px;
|
||||
margin-top: 36px;
|
||||
margin-left: -180px;
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
text-transform: none;
|
||||
font-size: 13px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.25);
|
||||
line-height: 18px;
|
||||
padding: 6px;
|
||||
transition: opacity .5s;
|
||||
font-weight: normal;
|
||||
text-align: left;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.Button:hover .tooltip {
|
||||
visibility: visible;
|
||||
opacity: 100%;
|
||||
transition: opacity .5s;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import styles from './ButtonRow.module.css'
|
||||
|
||||
const ButtonRow = (props) => {
|
||||
return <div className={styles.ButtonRow}>{props.children}</div>
|
||||
}
|
||||
|
||||
export default ButtonRow;
|
@ -0,0 +1,3 @@
|
||||
.ButtonRow {
|
||||
text-align: center;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import styles from './Card.module.css'
|
||||
|
||||
const Card = (props) => {
|
||||
return (<div className={styles.Card}>{props.children}</div>)
|
||||
}
|
||||
|
||||
export default Card;
|
@ -0,0 +1,22 @@
|
||||
.Card {
|
||||
border: var(--box-border);
|
||||
box-shadow: var(--box-shadow);
|
||||
border-radius: var(--border-radius);
|
||||
background-color: white;
|
||||
padding: 12px;
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
.Card h1 {
|
||||
margin: 12px -12px 0px -12px;
|
||||
padding: 6px 12px 0px 12px;
|
||||
font-size: 14px;
|
||||
line-height: 35px;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.Card h1:first-child {
|
||||
margin: -12px -12px 0px -12px;
|
||||
border-top: none;
|
||||
background: none;
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
import form_styles from './Form.module.css'
|
||||
import Button from './Button'
|
||||
|
||||
const DictInputRow = (props) => {
|
||||
let unit = "";
|
||||
if (props.unit) {
|
||||
unit = <span className={form_styles.FormRow_unit}>({props.unit})</span>
|
||||
}
|
||||
|
||||
let tooltip = "";
|
||||
if (props.tooltip != undefined) {
|
||||
tooltip = <Button label="?" kind="inline" tooltip={props.tooltip} />
|
||||
}
|
||||
|
||||
let value = {}
|
||||
if (props.value != undefined) {
|
||||
value = props.value;
|
||||
}
|
||||
if (props.disableKeys === undefined) {
|
||||
value[""] = "";
|
||||
}
|
||||
|
||||
const form = []
|
||||
Object.keys(value).forEach((key, index) => {
|
||||
let label = <span>{props.label} {unit}</span>;
|
||||
if (index > 0) {
|
||||
label = "";
|
||||
}
|
||||
form.push(
|
||||
<div className={form_styles.FormRow} key={index}>
|
||||
<label>{label}</label>
|
||||
<input
|
||||
type="text"
|
||||
data-index={index}
|
||||
value={key}
|
||||
placeholder={props.keyPlaceholder}
|
||||
disabled={props.disableKeys}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
data-index={index}
|
||||
value={value[key]}
|
||||
placeholder={props.valuePlaceholder}
|
||||
/>
|
||||
{tooltip}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return <>
|
||||
{form}
|
||||
</>;
|
||||
}
|
||||
|
||||
export default DictInputRow;
|
@ -0,0 +1,21 @@
|
||||
import form_styles from './Form.module.css'
|
||||
import Button from './Button'
|
||||
|
||||
const FileInputRow = (props) => {
|
||||
|
||||
let tooltip = "";
|
||||
if (props.tooltip != undefined) {
|
||||
tooltip = <Button label="?" kind="inline" tooltip={props.tooltip} />
|
||||
}
|
||||
|
||||
return <div className={form_styles.FormRow}>
|
||||
<label>{props.label}</label>
|
||||
<input type="text" disabled="disabled" />
|
||||
<Button label="Upload" kind="inline" />
|
||||
<Button label="Clear" kind="inline" />
|
||||
<Button label="Template" kind="inline" />
|
||||
{tooltip}
|
||||
</div>;
|
||||
}
|
||||
|
||||
export default FileInputRow;
|
@ -0,0 +1,10 @@
|
||||
import styles from './Footer.module.css'
|
||||
|
||||
const Footer = () => {
|
||||
return <div className={styles.Footer}>
|
||||
<p>RELOG: Reverse Logistics Optimization</p>
|
||||
<p>Copyright © 2020—2022, UChicago Argonne, LLC. All Rights Reserved.</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
export default Footer;
|
@ -0,0 +1,11 @@
|
||||
.Footer {
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
padding: 24px;
|
||||
margin-top: 24px;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
line-height: 8px;
|
||||
min-width: 900px;
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
const Form = (props) => {
|
||||
return <>{props.children}</>;
|
||||
}
|
||||
|
||||
export default Form;
|
@ -0,0 +1,23 @@
|
||||
.FormRow {
|
||||
display: flex;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.FormRow label {
|
||||
width: 350px;
|
||||
padding: 6px 12px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.FormRow input {
|
||||
flex: 1;
|
||||
font-family: monospace;
|
||||
border: var(--box-border);
|
||||
border-radius: var(--border-radius);
|
||||
padding: 4px;
|
||||
margin: 2px 3px;
|
||||
}
|
||||
|
||||
.FormRow_unit {
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import styles from './Header.module.css'
|
||||
|
||||
const Header = () => {
|
||||
return (
|
||||
<div className={styles.HeaderBox}>
|
||||
<div className={styles.HeaderContent}>
|
||||
<h1>RELOG</h1>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header;
|
@ -0,0 +1,19 @@
|
||||
.HeaderBox {
|
||||
background-color: white;
|
||||
border-bottom: var(--box-border);
|
||||
box-shadow: var(--box-shadow);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.HeaderContent {
|
||||
margin: 0 auto;
|
||||
max-width: var(--site-width);
|
||||
}
|
||||
|
||||
.HeaderContent h1 {
|
||||
line-height: 48px;
|
||||
font-size: 28px;
|
||||
padding: 12px;
|
||||
margin: 0;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import './index.css';
|
||||
import PipelineBlock from './PipelineBlock';
|
||||
import ParametersBlock from './ParametersBlock';
|
||||
import ProductBlock from './ProductBlock';
|
||||
import PlantBlock from './PlantBlock';
|
||||
import ButtonRow from './ButtonRow';
|
||||
import Button from './Button';
|
||||
|
||||
|
||||
const InputPage = () => {
|
||||
return <>
|
||||
<PipelineBlock />
|
||||
<ParametersBlock />
|
||||
<ProductBlock name="Battery" />
|
||||
<ProductBlock name="Nickel" />
|
||||
<ProductBlock name="Metal casing" />
|
||||
<PlantBlock name="Battery Recycling Plant" />
|
||||
<ButtonRow>
|
||||
<Button label="Load" />
|
||||
<Button label="Save" />
|
||||
</ButtonRow>
|
||||
</>
|
||||
}
|
||||
|
||||
export default InputPage;
|
@ -0,0 +1,36 @@
|
||||
import Section from './Section'
|
||||
import Card from './Card'
|
||||
import Form from './Form'
|
||||
import TextInputRow from './TextInputRow'
|
||||
|
||||
const ParametersBlock = () => {
|
||||
return (
|
||||
<>
|
||||
<Section title="Parameters" />
|
||||
<Card>
|
||||
<Form>
|
||||
<TextInputRow
|
||||
label="Time horizon"
|
||||
unit="years"
|
||||
tooltip="Number of years in the simulation."
|
||||
default="1"
|
||||
/>
|
||||
<TextInputRow
|
||||
label="Building period"
|
||||
unit="years"
|
||||
tooltip="List of years in which we are allowed to open new plants. For example, if this parameter is set to [1,2,3], we can only open plants during the first three years. By default, this equals [1]; that is, plants can only be opened during the first year."
|
||||
default="[1]"
|
||||
/>
|
||||
<TextInputRow
|
||||
label="Annual inflation rate"
|
||||
unit="%"
|
||||
tooltip="Rate of inflation applied to all costs."
|
||||
default="0"
|
||||
/>
|
||||
</Form>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ParametersBlock;
|
@ -0,0 +1,87 @@
|
||||
import React from 'react';
|
||||
import ReactFlow, { Background } from 'react-flow-renderer';
|
||||
import Section from './Section';
|
||||
import Card from './Card';
|
||||
import Button from './Button';
|
||||
import styles from './PipelineBlock.module.css';
|
||||
|
||||
const elements = [
|
||||
{
|
||||
id: '1',
|
||||
data: { label: 'Battery' },
|
||||
sourcePosition: 'right',
|
||||
targetPosition: 'left',
|
||||
position: { x: 100, y: 200 },
|
||||
className: styles.ProductNode,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
data: { label: "Battery Recycling Plant" },
|
||||
sourcePosition: 'right',
|
||||
targetPosition: 'left',
|
||||
position: { x: 500, y: 150 },
|
||||
className: styles.PlantNode,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
data: { label: 'Nickel' },
|
||||
sourcePosition: 'right',
|
||||
targetPosition: 'left',
|
||||
position: { x: 900, y: 100 },
|
||||
className: styles.ProductNode,
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
data: { label: 'Metal casing' },
|
||||
sourcePosition: 'right',
|
||||
targetPosition: 'left',
|
||||
position: { x: 900, y: 300 },
|
||||
className: styles.ProductNode,
|
||||
},
|
||||
{
|
||||
id: 'e1-2',
|
||||
source: '1',
|
||||
target: '2',
|
||||
animated: true,
|
||||
selectable: false,
|
||||
style: { stroke: "black" },
|
||||
},
|
||||
{
|
||||
id: 'e2-3',
|
||||
source: '2',
|
||||
target: '3',
|
||||
animated: true,
|
||||
selectable: false,
|
||||
style: { stroke: "black" },
|
||||
},
|
||||
{
|
||||
id: 'e2-4',
|
||||
source: '2',
|
||||
target: '4',
|
||||
animated: true,
|
||||
selectable: false,
|
||||
style: { stroke: "black" },
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
const PipelineBlock = () => {
|
||||
return (
|
||||
<>
|
||||
<Section title="Pipeline" />
|
||||
<Card>
|
||||
<div className={styles.PipelineBlock}>
|
||||
<ReactFlow elements={elements}>
|
||||
<Background />
|
||||
</ReactFlow>
|
||||
</div>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<Button label="Add product" kind="inline" />
|
||||
<Button label="Add plant" kind="inline" />
|
||||
</div>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default PipelineBlock;
|
@ -0,0 +1,21 @@
|
||||
.PipelineBlock {
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
.PlantNode, .ProductNode {
|
||||
border-color: rgba(0, 0, 0, 0.8);
|
||||
color: black;
|
||||
font-size: 13px;
|
||||
border-width: 1px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0px 2px 4px -3px black;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.PlantNode {
|
||||
background-color: #0df;
|
||||
}
|
||||
|
||||
.ProductNode {
|
||||
background-color: #f6f6f6;
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
import Section from './Section'
|
||||
import Card from './Card'
|
||||
import Form from './Form'
|
||||
import TextInputRow from './TextInputRow'
|
||||
import FileInputRow from './FileInputRow'
|
||||
import DictInputRow from './DictInputRow'
|
||||
|
||||
const PlantBlock = (props) => {
|
||||
const emissions = {
|
||||
"CO2": "0.05",
|
||||
"CH4": "0.01",
|
||||
"N2O": "0.04",
|
||||
}
|
||||
const output = {
|
||||
"Nickel": "0.5",
|
||||
"Metal casing": "0.35",
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Section title={props.name} />
|
||||
<Card>
|
||||
<Form>
|
||||
<h1>General information</h1>
|
||||
<FileInputRow
|
||||
label="Candidate locations"
|
||||
tooltip="A dictionary mapping the name of the location to a dictionary which describes the site characteristics."
|
||||
/>
|
||||
|
||||
|
||||
<h1>Inputs & Outputs</h1>
|
||||
<TextInputRow
|
||||
label="Input"
|
||||
tooltip="The name of the product that this plant takes as input. Only one input is accepted per plant."
|
||||
disabled="disabled"
|
||||
value="Battery"
|
||||
/>
|
||||
<DictInputRow
|
||||
label="Outputs"
|
||||
unit="tonne/tonne"
|
||||
tooltip="A dictionary specifying how many tonnes of each product is produced for each tonnes of input. If the plant does not output anything, this key may be omitted."
|
||||
value={output}
|
||||
disableKeys={true}
|
||||
default="0"
|
||||
/>
|
||||
|
||||
<h1>Capacity & costs</h1>
|
||||
<TextInputRow
|
||||
label="Minimum capacity"
|
||||
unit="tonne"
|
||||
tooltip="The minimum size of the plant."
|
||||
default="0"
|
||||
/>
|
||||
<TextInputRow
|
||||
label="Opening cost (min capacity)"
|
||||
unit="$"
|
||||
tooltip="The cost to open the plant at minimum capacity."
|
||||
default="0.00"
|
||||
/>
|
||||
<TextInputRow
|
||||
label="Fixed operating cost (min capacity)"
|
||||
unit="$"
|
||||
tooltip="The cost to keep the plant open, even if the plant doesn't process anything."
|
||||
default="0.00"
|
||||
/>
|
||||
<TextInputRow
|
||||
label="Maximum capacity"
|
||||
unit="tonne"
|
||||
tooltip="The maximum size of the plant."
|
||||
default="0"
|
||||
/>
|
||||
<TextInputRow
|
||||
label="Opening cost (max capacity)"
|
||||
unit="$"
|
||||
tooltip="The cost to open a plant of this size."
|
||||
default="0.00"
|
||||
/>
|
||||
<TextInputRow
|
||||
label="Fixed operating cost (max capacity)"
|
||||
unit="$"
|
||||
tooltip="The cost to keep the plant open, even if the plant doesn't process anything."
|
||||
default="0.00"
|
||||
/>
|
||||
<TextInputRow
|
||||
label="Variable operating cost"
|
||||
unit="$"
|
||||
tooltip="The cost that the plant incurs to process each tonne of input."
|
||||
default="0.00"
|
||||
/>
|
||||
<TextInputRow
|
||||
label="Energy expenditure"
|
||||
unit="GJ/tonne"
|
||||
tooltip="The energy required to process 1 tonne of the input."
|
||||
default="0"
|
||||
/>
|
||||
|
||||
<h1>Storage</h1>
|
||||
<TextInputRow
|
||||
label="Storage cost"
|
||||
unit="$/tonne"
|
||||
tooltip="The cost to store a tonne of input product for one time period."
|
||||
default="0.00"
|
||||
/>
|
||||
<TextInputRow
|
||||
label="Storage limit"
|
||||
unit="tonne"
|
||||
tooltip="The maximum amount of input product this plant can have in storage at any given time."
|
||||
default="0"
|
||||
/>
|
||||
|
||||
<h1>Disposal</h1>
|
||||
<DictInputRow
|
||||
label="Disposal cost"
|
||||
unit="$/tonne"
|
||||
tooltip="The cost to dispose of the product."
|
||||
value={output}
|
||||
disableKeys={true}
|
||||
/>
|
||||
<DictInputRow
|
||||
label="Disposal limit"
|
||||
unit="tonne"
|
||||
tooltip="The maximum amount that can be disposed of. If an unlimited amount can be disposed, this key may be omitted."
|
||||
value={output}
|
||||
disableKeys={true}
|
||||
/>
|
||||
|
||||
<h1>Emissions</h1>
|
||||
<DictInputRow
|
||||
label="Emissions"
|
||||
unit="tonne/tonne"
|
||||
tooltip="A dictionary mapping the name of each greenhouse gas, produced to process each tonne of input, to the amount of gas produced (in tonne)."
|
||||
value={emissions}
|
||||
keyPlaceholder="Emission name"
|
||||
valuePlaceholder="0"
|
||||
/>
|
||||
|
||||
</Form>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default PlantBlock;
|
@ -0,0 +1,64 @@
|
||||
import Section from './Section'
|
||||
import Card from './Card'
|
||||
import Form from './Form'
|
||||
import TextInputRow from './TextInputRow'
|
||||
import FileInputRow from './FileInputRow'
|
||||
|
||||
const ProductBlock = (props) => {
|
||||
return (
|
||||
<>
|
||||
<Section title={props.name} />
|
||||
<Card>
|
||||
<Form>
|
||||
<h1>General information</h1>
|
||||
<FileInputRow
|
||||
label="Initial amounts"
|
||||
tooltip="A dictionary mapping the name of each location to its description (see below). If this product is not initially available, this key may be omitted."
|
||||
/>
|
||||
<TextInputRow
|
||||
label="Acquisition cost"
|
||||
unit="$/tonne"
|
||||
tooltip="The cost to acquire one tonne of this product from collection centers. Does not apply to plant outputs."
|
||||
default="0.00"
|
||||
/>
|
||||
|
||||
<h1>Disposal</h1>
|
||||
<TextInputRow
|
||||
label="Disposal cost"
|
||||
unit="$/tonne"
|
||||
tooltip="The cost to dispose of one tonne of this product at a collection center, without further processing. Does not apply to plant outputs."
|
||||
default="0"
|
||||
/>
|
||||
<TextInputRow
|
||||
label="Disposal limit"
|
||||
unit="tonne"
|
||||
tooltip="The maximum amount of this product that can be disposed of across all collection centers, without further processing."
|
||||
default="0"
|
||||
/>
|
||||
|
||||
<h1>Transportation</h1>
|
||||
<TextInputRow
|
||||
label="Transportation cost"
|
||||
unit="$/km/tonne"
|
||||
tooltip="The cost to transport this product."
|
||||
default="0.00"
|
||||
/>
|
||||
<TextInputRow
|
||||
label="Transportation energy"
|
||||
unit="J/km/tonne"
|
||||
default="0"
|
||||
tooltip="The energy required to transport this product."
|
||||
/>
|
||||
<TextInputRow
|
||||
label="Transportation emissions"
|
||||
unit="J/km/tonne"
|
||||
tooltip="A dictionary mapping the name of each greenhouse gas, produced to transport one tonne of this product along one kilometer, to the amount of gas produced (in tonnes)."
|
||||
default="0"
|
||||
/>
|
||||
</Form>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductBlock;
|
@ -0,0 +1,7 @@
|
||||
import styles from './Section.module.css'
|
||||
|
||||
const Section = (props) => {
|
||||
return <h2 className={styles.Section}>{props.title}</h2>
|
||||
}
|
||||
|
||||
export default Section;
|
@ -0,0 +1,6 @@
|
||||
.Section {
|
||||
line-height: 36px;
|
||||
margin: 12px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
import form_styles from './Form.module.css'
|
||||
import Button from './Button'
|
||||
|
||||
const TextInputRow = (props) => {
|
||||
let unit = "";
|
||||
if (props.unit) {
|
||||
unit = <span className={form_styles.FormRow_unit}>({props.unit})</span>
|
||||
}
|
||||
|
||||
let tooltip = "";
|
||||
if (props.tooltip != undefined) {
|
||||
tooltip = <Button label="?" kind="inline" tooltip={props.tooltip} />
|
||||
}
|
||||
|
||||
return <div className={form_styles.FormRow}>
|
||||
<label>
|
||||
{props.label} {unit}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder={props.default}
|
||||
disabled={props.disabled}
|
||||
value={props.value}
|
||||
/>
|
||||
{tooltip}
|
||||
</div>;
|
||||
}
|
||||
|
||||
export default TextInputRow;
|
@ -0,0 +1,48 @@
|
||||
:root {
|
||||
--site-width: 1200px;
|
||||
--box-border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
--box-shadow: 0px 2px 4px -3px rgba(0, 0, 0, 0.2);
|
||||
--border-radius: 4px;
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #f6f6f6;
|
||||
color: rgba(0, 0, 0, 0.95);
|
||||
}
|
||||
|
||||
#content {
|
||||
max-width: var(--site-width);
|
||||
min-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.react-flow__node.selected {
|
||||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2) !important;
|
||||
border-width: 2px !important;
|
||||
margin-top: -1px !important;
|
||||
margin-left: -1px !important;
|
||||
border-radius: 8px !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.react-flow__handle {
|
||||
width: 8px !important;
|
||||
height: 8px !important;
|
||||
background-color: white !important;
|
||||
border: 1px solid black !important;
|
||||
}
|
||||
|
||||
.react-flow__handle-right {
|
||||
right: -5px !important;
|
||||
}
|
||||
|
||||
.react-flow__handle-left {
|
||||
left: -5px !important;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import Header from './Header';
|
||||
import InputPage from './InputPage';
|
||||
import Footer from './Footer'
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<Header />
|
||||
<div id="content">
|
||||
<InputPage />
|
||||
</div>
|
||||
<Footer />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
Loading…
Reference in new issue