Merge branch 'master' of https://git.keannu1.duckdns.org/keannu125/Borrowing-TrackerFrontend into feature/additional_styles

This commit is contained in:
Jerilyn Yare 2024-01-07 13:22:01 +08:00
commit 94d41b3194
11 changed files with 262 additions and 200 deletions

View file

@ -12,4 +12,6 @@ steps:
- npm run build - npm run build
- ssh root@10.0.10.4 'rm -rf /mnt/sda1/files/projects/borrowing_tracker_frontend/*' - ssh root@10.0.10.4 'rm -rf /mnt/sda1/files/projects/borrowing_tracker_frontend/*'
- scp -r dist/* root@10.0.10.4:/mnt/sda1/files/projects/borrowing_tracker_frontend - scp -r dist/* root@10.0.10.4:/mnt/sda1/files/projects/borrowing_tracker_frontend
when:
- branch: master
secrets: [SSH_KEY] secrets: [SSH_KEY]

View file

@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CITC Equipment Tracker</title> <title>CSM Equipment Tracker</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

45
package-lock.json generated
View file

@ -23,12 +23,15 @@
"react-redux": "^8.1.3", "react-redux": "^8.1.3",
"react-router-dom": "^6.18.0", "react-router-dom": "^6.18.0",
"react-toastify": "^9.1.3", "react-toastify": "^9.1.3",
"react-virtuoso": "^4.6.2",
"react-window": "^1.8.10",
"reactjs-popup": "^2.0.6", "reactjs-popup": "^2.0.6",
"styled-components": "^6.1.1" "styled-components": "^6.1.1"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.2.15", "@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7", "@types/react-dom": "^18.2.7",
"@types/react-window": "^1.8.8",
"@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0", "@typescript-eslint/parser": "^6.0.0",
"@vitejs/plugin-react": "^4.0.3", "@vitejs/plugin-react": "^4.0.3",
@ -1761,6 +1764,15 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"node_modules/@types/react-window": {
"version": "1.8.8",
"resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz",
"integrity": "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/scheduler": { "node_modules/@types/scheduler": {
"version": "0.16.5", "version": "0.16.5",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.5.tgz", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.5.tgz",
@ -3372,6 +3384,11 @@
"resolved": "https://registry.npmjs.org/media-engine/-/media-engine-1.0.3.tgz", "resolved": "https://registry.npmjs.org/media-engine/-/media-engine-1.0.3.tgz",
"integrity": "sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg==" "integrity": "sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg=="
}, },
"node_modules/memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
},
"node_modules/merge2": { "node_modules/merge2": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -3895,6 +3912,34 @@
"react-dom": ">=16.6.0" "react-dom": ">=16.6.0"
} }
}, },
"node_modules/react-virtuoso": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.6.2.tgz",
"integrity": "sha512-vvlqvzPif+MvBrJ09+hJJrVY0xJK9yran+A+/1iwY78k0YCVKsyoNPqoLxOxzYPggspNBNXqUXEcvckN29OxyQ==",
"engines": {
"node": ">=10"
},
"peerDependencies": {
"react": ">=16 || >=17 || >= 18",
"react-dom": ">=16 || >=17 || >= 18"
}
},
"node_modules/react-window": {
"version": "1.8.10",
"resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz",
"integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==",
"dependencies": {
"@babel/runtime": "^7.0.0",
"memoize-one": ">=3.1.1 <6"
},
"engines": {
"node": ">8.0.0"
},
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/reactjs-popup": { "node_modules/reactjs-popup": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.6.tgz", "resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.6.tgz",

View file

@ -25,12 +25,15 @@
"react-redux": "^8.1.3", "react-redux": "^8.1.3",
"react-router-dom": "^6.18.0", "react-router-dom": "^6.18.0",
"react-toastify": "^9.1.3", "react-toastify": "^9.1.3",
"react-virtuoso": "^4.6.2",
"react-window": "^1.8.10",
"reactjs-popup": "^2.0.6", "reactjs-popup": "^2.0.6",
"styled-components": "^6.1.1" "styled-components": "^6.1.1"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.2.15", "@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7", "@types/react-dom": "^18.2.7",
"@types/react-window": "^1.8.8",
"@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0", "@typescript-eslint/parser": "^6.0.0",
"@vitejs/plugin-react": "^4.0.3", "@vitejs/plugin-react": "^4.0.3",

View file

@ -27,7 +27,7 @@ const debug = false;
let backendURL; let backendURL;
if (debug) { if (debug) {
backendURL = "http://localhost:8000/"; backendURL = "http://localhost:8092/";
} else { } else {
backendURL = "https://csm-backend.keannu1.duckdns.org/"; backendURL = "https://csm-backend.keannu1.duckdns.org/";
} }

View file

@ -6,7 +6,6 @@ import {
UserAPI, UserAPI,
TransactionsAPI, TransactionsAPI,
} from "../../API/API"; } from "../../API/API";
import CircularProgress from "@mui/material/CircularProgress";
import moment from "moment"; import moment from "moment";
export default function TechnicianWidgets() { export default function TechnicianWidgets() {
@ -34,22 +33,6 @@ export default function TechnicianWidgets() {
}, },
], ],
}); });
const isLoading = queries.some((result) => result.isLoading);
if (isLoading) {
return (
<>
<CircularProgress style={{ height: "128px", width: "128px" }} />
<p
style={{
...styles.text_dark,
...styles.text_L,
}}
>
Loading
</p>
</>
);
}
return ( return (
<div style={styles.flex_column}> <div style={styles.flex_column}>
<div <div
@ -92,7 +75,7 @@ export default function TechnicianWidgets() {
> >
{queries[1].data?.filter( {queries[1].data?.filter(
(equipment) => equipment.status == "Pending" (equipment) => equipment.status == "Pending"
).length || 0} ).length || "Loading..."}
</p> </p>
</div> </div>
<div <div
@ -123,7 +106,7 @@ export default function TechnicianWidgets() {
...styles.text_L, ...styles.text_L,
}} }}
> >
{queries[1].data?.length || 0} {queries[1].data?.length || "Loading..."}
</p> </p>
</div> </div>
</div> </div>
@ -167,7 +150,7 @@ export default function TechnicianWidgets() {
> >
{queries[1].data?.filter( {queries[1].data?.filter(
(equipment) => equipment.status == "Available" (equipment) => equipment.status == "Available"
).length || 0} ).length || "Loading..."}
</p> </p>
</div> </div>
<div <div
@ -200,7 +183,7 @@ export default function TechnicianWidgets() {
> >
{queries[1].data?.filter( {queries[1].data?.filter(
(equipment) => equipment.status == "Broken" (equipment) => equipment.status == "Broken"
).length || 0} ).length || "Loading..."}
</p> </p>
</div> </div>
</div> </div>
@ -247,7 +230,7 @@ export default function TechnicianWidgets() {
todayStartOfDay, todayStartOfDay,
todayEndOfDay todayEndOfDay
) )
).length || 0} ).length || "Loading..."}
</p> </p>
</div> </div>
<div <div
@ -283,7 +266,7 @@ export default function TechnicianWidgets() {
thisMonthStart, thisMonthStart,
thisMonthEnd thisMonthEnd
) )
).length || 0} ).length || "Loading..."}
</p> </p>
</div> </div>
</div> </div>

View file

@ -72,20 +72,6 @@ export default function TransactionReportPDF(props: props) {
).length ).length
} }
</Text> </Text>
<Text
style={{
color: colors.font_dark,
fontSize: 16,
textAlign: "center",
}}
>
Rejected Transactions:{" "}
{
transactions_today.filter(
(transaction) => transaction.transaction_status == "Rejected"
).length
}
</Text>
<Text <Text
style={{ style={{
color: colors.font_dark, color: colors.font_dark,

View file

@ -20,6 +20,7 @@ import {
CircularProgress, CircularProgress,
MenuItem, MenuItem,
OutlinedInput, OutlinedInput,
Autocomplete,
} from "@mui/material"; } from "@mui/material";
import React from "react"; import React from "react";
import Header from "../../Components/Header/Header"; import Header from "../../Components/Header/Header";
@ -32,10 +33,10 @@ export default function AddTransactionPage() {
equipments: [] as number[], equipments: [] as number[],
teacher: 0, teacher: 0,
subject: "", subject: "",
remarks: " ", remarks: "",
transaction_status: "Pending Approval", transaction_status: "Pending Approval",
consumables: " ", consumables: "",
additional_members: " ", additional_members: "",
borrower: 0, borrower: 0,
}); });
/* /*
@ -126,29 +127,50 @@ export default function AddTransactionPage() {
<div style={{...styles.flex_column, marginLeft: "1rem", marginRight: "1rem"}}> <div style={{...styles.flex_column, marginLeft: "1rem", marginRight: "1rem"}}>
<FormControl style={{ marginTop: "8px" }}> <FormControl style={{ marginTop: "8px" }}>
<FormLabel style={{...styles.text_dark, ...styles.bform_label2}}>Items Requested</FormLabel> <FormLabel
<Select style={{
...{...styles.text_dark, ...styles.bform_label2},
...{ marginLeft: "4px", marginBottom: "8px", textAlign: "left" },
}}
>
Items Requested
</FormLabel>
<Autocomplete
multiple multiple
value={transaction.equipments} id="equipment-autocomplete"
onChange={(event) => options={equipments.data?.sort((a, b) => a.id - b.id) || []}
getOptionLabel={(option) =>
`${option.equipment_name} (ID:${option.id})`
}
value={
equipments.data?.filter((equipment) =>
transaction.equipments.includes(equipment.id)
) || []
}
onChange={(_event, newValue) => {
SetTransaction({ SetTransaction({
...transaction, ...transaction,
equipments: event.target.value as number[], equipments: newValue.map((item) => item.id),
}) });
} }}
input={<OutlinedInput />} renderInput={(params) => (
> <TextField
{equipments.data {...params}
?.filter((equipment) => equipment.status == "Available") variant="outlined"
.map((equipment) => ( label="Items Requested"
<MenuItem key={equipment.id} value={equipment.id}> />
{`${equipment.equipment_name} (ID:${equipment.id})`} )}
</MenuItem> />
))}
</Select>
</FormControl> </FormControl>
<FormControl style={{ marginTop: "8px" }}> <FormControl style={{ marginTop: "8px" }}>
<FormLabel style={{...styles.text_dark, ...styles.bform_label2}}>Assigned Teacher</FormLabel> <FormLabel
style={{
...styles.text_dark,
...{ marginLeft: "4px", marginBottom: "4px", textAlign: "left" },
}}
>
Assigned Teacher
</FormLabel>
<Select <Select
value={transaction.teacher} value={transaction.teacher}
onChange={(event) => onChange={(event) =>
@ -157,9 +179,7 @@ export default function AddTransactionPage() {
teacher: event.target.value as number, teacher: event.target.value as number,
}) })
} }
label={"Assigned Teacher"}
input={<OutlinedInput />} input={<OutlinedInput />}
> >
{teachers.data?.map((teacher) => ( {teachers.data?.map((teacher) => (
<MenuItem key={teacher.id} value={teacher.id}> <MenuItem key={teacher.id} value={teacher.id}>
@ -169,7 +189,14 @@ export default function AddTransactionPage() {
</Select> </Select>
</FormControl> </FormControl>
<FormControl style={{ marginTop: "8px" }}> <FormControl style={{ marginTop: "8px" }}>
<FormLabel style={{...styles.text_dark, ...styles.bform_label}}>Subject</FormLabel> <FormLabel
style={{
...styles.text_dark,
...{ marginLeft: "4px", marginBottom: "4px", textAlign: "left" },
}}
>
Subject
</FormLabel>
<TextField <TextField
style={styles.input_form} style={styles.input_form}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => { onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
@ -181,7 +208,14 @@ export default function AddTransactionPage() {
/> />
</FormControl> </FormControl>
<FormControl style={{ marginTop: "8px" }}> <FormControl style={{ marginTop: "8px" }}>
<FormLabel style={{...styles.text_dark, ...styles.bform_label}}>Remarks</FormLabel> <FormLabel
style={{
...styles.text_dark,
...{ marginLeft: "4px", marginBottom: "4px", textAlign: "left" },
}}
>
Remarks
</FormLabel>
<TextField <TextField
multiline multiline
style={styles.input_form} style={styles.input_form}
@ -194,7 +228,14 @@ export default function AddTransactionPage() {
/> />
</FormControl> </FormControl>
<FormControl style={{ marginTop: "8px" }}> <FormControl style={{ marginTop: "8px" }}>
<FormLabel style={{...styles.text_dark, ...styles.bform_label}}>Consumables</FormLabel> <FormLabel
style={{
...styles.text_dark,
...{ marginLeft: "4px", marginBottom: "4px", textAlign: "left" },
}}
>
Consumables
</FormLabel>
<TextField <TextField
multiline multiline
style={styles.input_form} style={styles.input_form}
@ -207,7 +248,14 @@ export default function AddTransactionPage() {
/> />
</FormControl> </FormControl>
<FormControl style={{ marginTop: "8px" }}> <FormControl style={{ marginTop: "8px" }}>
<FormLabel style={{...styles.text_dark, ...styles.bform_label}}>Additional Members</FormLabel> <FormLabel
style={{
...styles.text_dark,
...{ marginLeft: "4px", marginBottom: "4px", textAlign: "left" },
}}
>
Additional Members
</FormLabel>
<TextField <TextField
multiline multiline
style={styles.input_form} style={styles.input_form}
@ -261,6 +309,9 @@ export default function AddTransactionPage() {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: ["equipment_instances"], queryKey: ["equipment_instances"],
}); });
queryClient.invalidateQueries({
queryKey: ["equipment_instances_available"],
});
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: ["transactions"], queryKey: ["transactions"],
}); });

View file

@ -81,7 +81,6 @@ export default function EquipmentInstancesListPage() {
width: "100%", width: "100%",
minHeight: "100%", minHeight: "100%",
minWidth: "100%", minWidth: "100%",
flexWrap: "wrap",
}} }}
> >
{/* ADDED/INSERTED BUTTON,POPUP, TWO CATEGORY BUTTONS*/} {/* ADDED/INSERTED BUTTON,POPUP, TWO CATEGORY BUTTONS*/}
@ -200,76 +199,73 @@ export default function EquipmentInstancesListPage() {
</p> </p>
</Button> </Button>
<div style={{ alignSelf: "flex-start", paddingLeft: "32px" }}> <div
<div style={{
...styles.flex_row,
...{ alignItems: "center", justifySelf: "flex-start" },
}}
>
<SearchIcon
style={{ style={{
...styles.flex_row, height: 32,
...{ alignItems: "center", justifySelf: "flex-start" }, width: 32,
fill: colors.font_dark,
marginLeft: "1rem",
marginRight: "1rem",
}}
/>
<Autocomplete
sx={{
display: "inline-block",
"& input": {
width: "256x",
bgcolor: "background.paper",
color: (theme) =>
theme.palette.getContrastText(theme.palette.background.paper),
},
}}
value={filter}
onChange={(_event, newValue) => {
setFilter(newValue);
}}
freeSolo
id="custom-input-demo"
options={["Available", "Broken", "Glassware", "Miscellaneous"]}
renderInput={(params) => (
<div ref={params.InputProps.ref}>
<input type="text" {...params.inputProps} />
</div>
)}
/>
<p
style={{
...styles.text_M,
...styles.text_dark,
...{ marginLeft: "4px" },
}} }}
> >
<SearchIcon Results Found:{" "}
style={{ {
height: 32, equipment_instances?.data?.filter((equipment) =>
width: 32, filter !== null
fill: colors.font_dark, ? // If filter is not null, we filter if it matches any criteria
marginLeft: "1rem", equipment.equipment_name
marginRight: "1rem", .toLowerCase()
}} .includes(filter.toLowerCase()) ||
/> equipment.category
<Autocomplete .toLowerCase()
sx={{ .includes(filter.toLowerCase()) ||
display: "inline-block", equipment.last_updated
"& input": { .toLowerCase()
width: "256x", .includes(filter?.toLowerCase()) ||
bgcolor: "background.paper", equipment.status.toLowerCase() == filter.toLowerCase()
color: (theme) => : // If filter keyword is null then we just pass through everything as if we did not filter at all
theme.palette.getContrastText( true
theme.palette.background.paper ).length
), }
}, </p>
}}
value={filter}
onChange={(_event, newValue) => {
setFilter(newValue);
}}
freeSolo
id="custom-input-demo"
options={["Available", "Broken", "Glassware", "Miscellaneous"]}
renderInput={(params) => (
<div ref={params.InputProps.ref}>
<input type="text" {...params.inputProps} />
</div>
)}
/>
<p
style={{
...styles.text_M,
...styles.text_dark,
...{ marginLeft: "4px" },
}}
>
Results Found:{" "}
{
equipment_instances?.data?.filter((equipment) =>
filter !== null
? // If filter is not null, we filter if it matches any criteria
equipment.equipment_name
.toLowerCase()
.includes(filter.toLowerCase()) ||
equipment.category
.toLowerCase()
.includes(filter.toLowerCase()) ||
equipment.last_updated
.toLowerCase()
.includes(filter?.toLowerCase()) ||
equipment.status.toLowerCase() == filter.toLowerCase()
: // If filter keyword is null then we just pass through everything as if we did not filter at all
true
).length
}
</p>
</div>
</div> </div>
<div style={{ alignSelf: "flex-start", paddingLeft: "32px" }}></div>
<TableContainer <TableContainer
style={{ width: "90%", overflowY: "scroll", marginTop: "2rem" }} style={{ width: "90%", overflowY: "scroll", marginTop: "2rem" }}
component={Paper} component={Paper}

View file

@ -65,79 +65,75 @@ export default function EquipmentTallyPage() {
width: "100%", width: "100%",
minHeight: "100%", minHeight: "100%",
minWidth: "100%", minWidth: "100%",
flexWrap: "wrap",
}} }}
> >
<div style={{ alignSelf: "flex-start", paddingLeft: "32px" }}> <div
<div style={{
...styles.flex_row,
...{ alignItems: "center", justifySelf: "flex-start" },
}}
>
<SearchIcon
style={{ style={{
...styles.flex_row, height: 32,
...{ alignItems: "center", justifySelf: "flex-start" }, width: 32,
fill: colors.font_dark,
marginLeft: "1rem",
marginRight: "1rem",
}}
/>
<Autocomplete
sx={{
display: "inline-block",
"& input": {
width: "256x",
bgcolor: "background.paper",
color: (theme) =>
theme.palette.getContrastText(theme.palette.background.paper),
},
}}
value={filter}
onChange={(_event, newValue) => {
setFilter(newValue);
}}
freeSolo
id="custom-input-demo"
options={["Glassware", "Miscellaneous"]}
renderInput={(params) => (
<div ref={params.InputProps.ref}>
<input type="text" {...params.inputProps} />
</div>
)}
/>
<p
style={{
...styles.text_M,
...styles.text_dark,
...{ marginLeft: "4px" },
}} }}
> >
<SearchIcon Results Found:{" "}
style={{ {
height: 32, equipment_instances?.data?.filter((equipment) =>
width: 32, filter !== null
fill: colors.font_dark, ? // If filter is not null, we filter if it matches any criteria
marginLeft: "1rem", equipment.equipment_name
marginRight: "1rem", .toLowerCase()
}} .includes(filter.toLowerCase()) ||
/> equipment.category
<Autocomplete .toLowerCase()
sx={{ .includes(filter.toLowerCase()) ||
display: "inline-block", equipment.last_updated
"& input": { .toLowerCase()
width: "256x", .includes(filter?.toLowerCase()) ||
bgcolor: "background.paper", equipment.status.toLowerCase() == filter.toLowerCase()
color: (theme) => : // If filter keyword is null then we just pass through everything as if we did not filter at all
theme.palette.getContrastText( true
theme.palette.background.paper ).length
), }
}, </p>
}}
value={filter}
onChange={(_event, newValue) => {
setFilter(newValue);
}}
freeSolo
id="custom-input-demo"
options={["Glassware", "Miscellaneous"]}
renderInput={(params) => (
<div ref={params.InputProps.ref}>
<input type="text" {...params.inputProps} />
</div>
)}
/>
<p
style={{
...styles.text_M,
...styles.text_dark,
...{ marginLeft: "4px" },
}}
>
Results Found:{" "}
{
equipments?.data?.filter((equipment) =>
filter !== null
? // If filter is not null, we filter if it matches any criteria
equipment.name
.toLowerCase()
.includes(filter.toLowerCase()) ||
equipment.category
.toLowerCase()
.includes(filter.toLowerCase()) ||
equipment.last_updated
.toLowerCase()
.includes(filter?.toLowerCase()) ||
equipment.category.toLowerCase() == filter.toLowerCase()
: // If filter keyword is null then we just pass through everything as if we did not filter at all
true
).length
}
</p>
</div>
</div> </div>
<div style={{ alignSelf: "flex-start", paddingLeft: "32px" }}></div>
<TableContainer <TableContainer
style={{ width: "90%", overflowY: "scroll", marginTop: "2rem" }} style={{ width: "90%", overflowY: "scroll", marginTop: "2rem" }}
component={Paper} component={Paper}