- Use React beautiful dnd library for servers list section
- The dnded servers list order can be sent for persistence now - Removed the previous React DND and Html5 Backend libraries as it is more lower level and hard to enable the drag indicator within AutoComplete
This commit is contained in:
1 parent
b5409f0c0f
commit
e8a9c0da36
10 files changed
+309
-285
No files matched your search
Generated
+98
-58
@@ -25,8 +25,7 @@
|
||||
"jwt-decode": "^4.0.0",
|
||||
"mui-color-input": "^5.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-redux": "9.2.0",
|
||||
"react-router": "^6.23.0",
|
||||
@@ -1630,21 +1629,6 @@
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-dnd/asap": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz",
|
||||
"integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A=="
|
||||
},
|
||||
"node_modules/@react-dnd/invariant": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz",
|
||||
"integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw=="
|
||||
},
|
||||
"node_modules/@react-dnd/shallowequal": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz",
|
||||
"integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA=="
|
||||
},
|
||||
"node_modules/@react-spring/animated": {
|
||||
"version": "9.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.5.tgz",
|
||||
@@ -2386,6 +2370,15 @@
|
||||
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/hoist-non-react-statics": {
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz",
|
||||
"integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==",
|
||||
"dependencies": {
|
||||
"@types/react": "*",
|
||||
"hoist-non-react-statics": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
|
||||
@@ -2418,6 +2411,25 @@
|
||||
"@types/react": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-redux": {
|
||||
"version": "7.1.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.34.tgz",
|
||||
"integrity": "sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==",
|
||||
"dependencies": {
|
||||
"@types/hoist-non-react-statics": "^3.3.0",
|
||||
"@types/react": "*",
|
||||
"hoist-non-react-statics": "^3.3.0",
|
||||
"redux": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-redux/node_modules/redux": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-transition-group": {
|
||||
"version": "4.4.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
|
||||
@@ -2959,6 +2971,14 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/css-box-model": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
|
||||
"integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
|
||||
"dependencies": {
|
||||
"tiny-invariant": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
@@ -3242,24 +3262,6 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dnd-core": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz",
|
||||
"integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==",
|
||||
"dependencies": {
|
||||
"@react-dnd/asap": "^5.0.1",
|
||||
"@react-dnd/invariant": "^4.0.1",
|
||||
"redux": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dnd-core/node_modules/redux": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/doctrine": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
||||
@@ -3830,6 +3832,7 @@
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-equals": {
|
||||
@@ -4974,6 +4977,11 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"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/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
@@ -5452,6 +5460,11 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/raf-schd": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
|
||||
"integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ=="
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
@@ -5464,41 +5477,60 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dnd": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz",
|
||||
"integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==",
|
||||
"node_modules/react-beautiful-dnd": {
|
||||
"version": "13.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz",
|
||||
"integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==",
|
||||
"deprecated": "react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672",
|
||||
"dependencies": {
|
||||
"@react-dnd/invariant": "^4.0.1",
|
||||
"@react-dnd/shallowequal": "^4.0.1",
|
||||
"dnd-core": "^16.0.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"hoist-non-react-statics": "^3.3.2"
|
||||
"@babel/runtime": "^7.9.2",
|
||||
"css-box-model": "^1.2.0",
|
||||
"memoize-one": "^5.1.1",
|
||||
"raf-schd": "^4.0.2",
|
||||
"react-redux": "^7.2.0",
|
||||
"redux": "^4.0.4",
|
||||
"use-memo-one": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/hoist-non-react-statics": ">= 3.3.1",
|
||||
"@types/node": ">= 12",
|
||||
"@types/react": ">= 16",
|
||||
"react": ">= 16.14"
|
||||
"react": "^16.8.5 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-beautiful-dnd/node_modules/react-is": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
||||
},
|
||||
"node_modules/react-beautiful-dnd/node_modules/react-redux": {
|
||||
"version": "7.2.9",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz",
|
||||
"integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.15.4",
|
||||
"@types/react-redux": "^7.1.20",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^17.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.3 || ^17 || ^18"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/hoist-non-react-statics": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react": {
|
||||
"react-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-dnd-html5-backend": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz",
|
||||
"integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==",
|
||||
"node_modules/react-beautiful-dnd/node_modules/redux": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
|
||||
"dependencies": {
|
||||
"dnd-core": "^16.0.1"
|
||||
"@babel/runtime": "^7.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
@@ -6420,6 +6452,14 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-memo-one": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz",
|
||||
"integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
|
||||
|
||||
+1
-2
@@ -28,8 +28,7 @@
|
||||
"jwt-decode": "^4.0.0",
|
||||
"mui-color-input": "^5.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-redux": "9.2.0",
|
||||
"react-router": "^6.23.0",
|
||||
|
||||
@@ -67,10 +67,12 @@ PasswordEndAdornment.propTypes = {
|
||||
setFieldType: PropTypes.func,
|
||||
};
|
||||
|
||||
export const ServerStartAdornment = () => {
|
||||
export const ServerStartAdornment = (props) => {
|
||||
return (
|
||||
<InputAdornment position="start">
|
||||
<ReorderRoundedIcon />
|
||||
<Stack {...props.dragHandleProps}>
|
||||
<ReorderRoundedIcon />
|
||||
</Stack>
|
||||
</InputAdornment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export default {
|
||||
CARD: 'card',
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
import {
|
||||
ServerEndAdornment,
|
||||
} from "../../../../Inputs/TextInput/Adornments";
|
||||
import Search from "../../../../Inputs/Search";
|
||||
import { useState } from "react";
|
||||
import ReorderRoundedIcon from "@mui/icons-material/ReorderRounded";
|
||||
import { Stack, IconButton } from "@mui/material";
|
||||
import React from "react";
|
||||
/**
|
||||
*
|
||||
* @param {*} id The Id of the Server component
|
||||
* @param {*} removeItem The function used to remove a single server
|
||||
* @param {*} onChange The Change handler function to handle when the server value is changed
|
||||
* used to update the server(monitor) lists
|
||||
* @param {*} monitors The server monitors options
|
||||
* @param {*} value the option label of the server/monitor, namely the monitor name field
|
||||
* @returns
|
||||
*/
|
||||
const Server = (props) => {
|
||||
const [search, setSearch] = useState("");
|
||||
const handleSearch = (val) => {
|
||||
setSearch(val);
|
||||
};
|
||||
return (
|
||||
<Stack direction={"row"}>
|
||||
<IconButton>
|
||||
<ReorderRoundedIcon />
|
||||
</IconButton>
|
||||
<Search
|
||||
id={props.id}
|
||||
multiple={false}
|
||||
isAdorned={false}
|
||||
options={props.monitors ? props.monitors : []}
|
||||
filteredBy="name"
|
||||
inputValue={search}
|
||||
value={props.value}
|
||||
endAdornment={
|
||||
<ServerEndAdornment
|
||||
id={props.id}
|
||||
removeItem={props.removeItem}
|
||||
/>
|
||||
}
|
||||
handleInputChange={handleSearch}
|
||||
handleChange={props.onChange}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default Server;
|
||||
@@ -1,109 +0,0 @@
|
||||
import React, { useRef } from "react";
|
||||
import { useDrag, useDrop } from "react-dnd";
|
||||
import ItemTypes from "./ItemTypes";
|
||||
import Card from "@mui/material/Card";
|
||||
import Server from "./Server"
|
||||
import { CardContent } from "@mui/material";
|
||||
|
||||
/**
|
||||
* CustomCard is a React Forward Ref component that
|
||||
* passes properties to its wrapped component with
|
||||
* ref that carrys the drag and drop feature etc
|
||||
*/
|
||||
const CustomCard = React.forwardRef((props, ref) => (
|
||||
<Card ref={ref}>
|
||||
<CardContent>
|
||||
<Server
|
||||
id={props.id}
|
||||
removeItem={props.removeCard}
|
||||
value={props.value}
|
||||
onChange={props.onChange}
|
||||
monitors={props.monitors}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
));
|
||||
|
||||
/**
|
||||
* @param {*} id The Id of the Server component
|
||||
* @param {*} index The index of the Card in its natural order of placement
|
||||
* @param {*} moveCard The function to actually move a card
|
||||
* @param {*} removeCard The function to remote the card
|
||||
* @param {*} onChange The Change handler function to handle when the server value is changed
|
||||
* used to update the server(monitor) lists
|
||||
* @param {*} monitors The server monitors options
|
||||
* @param {*} value the option label of the server/monitor, namely the monitor name field
|
||||
* @returns The Card component representing a singler server/monitor that is capable of being
|
||||
* dragged and droped
|
||||
*/
|
||||
|
||||
const MyCard = ({ id, index, moveCard, removeCard, value, onChange, monitors }) => {
|
||||
const ref = useRef(null);
|
||||
const [, drop] = useDrop({
|
||||
accept: ItemTypes.CARD,
|
||||
hover(item, monitor) {
|
||||
if (!ref.current) {
|
||||
return;
|
||||
}
|
||||
const dragIndex = item.index;
|
||||
const hoverIndex = index;
|
||||
|
||||
// Don't replace items with themselves
|
||||
if (dragIndex === hoverIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine rectangle on screen
|
||||
const hoverBoundingRect = ref.current.getBoundingClientRect();
|
||||
// Get vertical middle
|
||||
const hoverMiddleY =
|
||||
(hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
|
||||
// Determine mouse position
|
||||
const clientOffset = monitor.getClientOffset();
|
||||
// Get pixels to the top
|
||||
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
|
||||
// Only perform the move when the mouse has crossed half of the items height
|
||||
// When dragging downwards, only move when the cursor is below 50%
|
||||
// When dragging upwards, only move when the cursor is above 50%
|
||||
// Dragging downwards
|
||||
|
||||
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
|
||||
return;
|
||||
}
|
||||
// Dragging upwards
|
||||
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Time to actually perform the action
|
||||
moveCard(dragIndex, hoverIndex);
|
||||
// Note: we're mutating the monitor item here!
|
||||
// Generally it's better to avoid mutations,
|
||||
// but it's good here for the sake of performance
|
||||
// to avoid expensive index searches.
|
||||
item.index = hoverIndex;
|
||||
}
|
||||
});
|
||||
const [ _, drag] = useDrag({
|
||||
type: ItemTypes.CARD,
|
||||
item: {id: id, index:index},
|
||||
collect: monitor => ({
|
||||
isDragging: monitor.isDragging()
|
||||
})
|
||||
});
|
||||
drag(drop(ref));
|
||||
return (
|
||||
<CustomCard
|
||||
ref={ref}
|
||||
id={id}
|
||||
removeCard={removeCard}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
monitors={monitors}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default MyCard;
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { useState, useCallback, useContext, useEffect } from "react";
|
||||
import { useState, useContext, useEffect } from "react";
|
||||
import { Button, Box, Stack, Typography } from "@mui/material";
|
||||
import ConfigBox from "../../../Components/ConfigBox";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import TabPanel from "@mui/lab/TabPanel";
|
||||
import Card from "./Card";
|
||||
import update from "immutability-helper";
|
||||
import { StatusFormContext } from "../../../Pages/Status/CreateStatusContext";
|
||||
import { useSelector } from "react-redux";
|
||||
import { logger } from "../../../Utils/Logger"
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import { networkService } from "../../../main";
|
||||
import ServersList from "./ServersList";
|
||||
|
||||
|
||||
/**
|
||||
* Content Panel is used to compose the second part of the status page
|
||||
@@ -24,6 +24,7 @@ const ContentPanel = () => {
|
||||
const {user, authToken } = useSelector((state) => state.auth);
|
||||
const [monitors, setMonitors] = useState([]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const fetchMonitors = async () => {
|
||||
try {
|
||||
@@ -54,44 +55,6 @@ const ContentPanel = () => {
|
||||
};
|
||||
fetchMonitors();
|
||||
}, [user, authToken]);
|
||||
|
||||
const moveCard = useCallback(
|
||||
(dragIndex, hoverIndex) => {
|
||||
const dragCard = cards[dragIndex];
|
||||
setCards(
|
||||
update(cards, {
|
||||
$splice: [
|
||||
[dragIndex, 1],
|
||||
[hoverIndex, 0, dragCard],
|
||||
],
|
||||
})
|
||||
);
|
||||
},
|
||||
[cards]
|
||||
);
|
||||
|
||||
const handleCardChange = (event, val) => {
|
||||
event.preventDefault();
|
||||
const { id } = event.target;
|
||||
let idx = cards.findIndex((a) => {
|
||||
let found = false;
|
||||
let optionIdx = id.indexOf("-option");
|
||||
if (optionIdx !== -1) found = a.id == id.substr(0, optionIdx);
|
||||
else found = a.id == id;
|
||||
return found;
|
||||
});
|
||||
let newCards;
|
||||
if (idx >= 0) {
|
||||
let x = JSON.parse(JSON.stringify(cards[idx]));
|
||||
x.val = val;
|
||||
newCards = update(cards, { $splice: [[idx, 1, x]] });
|
||||
setCards(newCards);
|
||||
} else {
|
||||
newCards = update(cards, { $push: [{ id: id, val: val }] });
|
||||
setCards(newCards);
|
||||
}
|
||||
setForm({ ...form, monitors: newCards.map(c=>c.val) });
|
||||
};
|
||||
const handleAddNew = () => {
|
||||
if (cards.length === monitors.length) return;
|
||||
const newCards = [...cards, { id: "" + Math.random(),val:{} }];
|
||||
@@ -172,21 +135,14 @@ const ContentPanel = () => {
|
||||
direction="column"
|
||||
gap={theme.spacing(2)}
|
||||
>
|
||||
{cards.map((card, idx) => {
|
||||
return (
|
||||
<Card
|
||||
key={idx}
|
||||
index={idx}
|
||||
id={card?.id ?? "" + Math.random()}
|
||||
text={"" + idx}
|
||||
moveCard={moveCard}
|
||||
removeCard={removeCard}
|
||||
value={card.val ?? {}}
|
||||
onChange={handleCardChange}
|
||||
monitors={monitors}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<ServersList
|
||||
monitors={monitors}
|
||||
cards={cards}
|
||||
setCards={setCards}
|
||||
form={form}
|
||||
setForm={setForm}
|
||||
removeItem={removeCard}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
{errors["monitors"] && (
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { ServerStartAdornment, ServerEndAdornment } from "../../../../Inputs/TextInput/Adornments";
|
||||
import Search from "../../../../Inputs/Search";
|
||||
import {useState} from "react"
|
||||
import React from "react";
|
||||
import { Stack } from "@mui/material";
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} id The Id of the Server component
|
||||
* @param {*} monitors The server monitors options
|
||||
* @param {*} value the option label of the server/monitor, namely the monitor name field
|
||||
* @param {*} removeItem The function used to remove a single server
|
||||
* @param {*} onChange The Change handler function to handle when the server value is changed
|
||||
* used to update the server(monitor) lists*
|
||||
* @param {*} dragHandleProps the dragHandleProps passed on to the designated dom node
|
||||
* so that there will be a draging indicator when mouse over it
|
||||
* @returns A single server whose value is one of the existing monitors
|
||||
*/
|
||||
|
||||
const Server = ({ id, value, monitors, onChange, removeItem, dragHandleProps }) => {
|
||||
const [search, setSearch] = useState("");
|
||||
const handleSearch = (val) => {
|
||||
setSearch(val);
|
||||
};
|
||||
return (
|
||||
<Stack direction={"row"}>
|
||||
<Search
|
||||
id={id}
|
||||
multiple={false}
|
||||
isAdorned={false}
|
||||
options={monitors ? monitors : []}
|
||||
filteredBy="name"
|
||||
inputValue={search}
|
||||
value={value}
|
||||
startAdornment={<ServerStartAdornment dragHandleProps={dragHandleProps} />}
|
||||
endAdornment={
|
||||
<ServerEndAdornment
|
||||
id={id}
|
||||
removeItem={removeItem}
|
||||
/>
|
||||
}
|
||||
handleInputChange={handleSearch}
|
||||
handleChange={onChange}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
Server.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
monitors: PropTypes.array.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
removeItem: PropTypes.function.isRequired,
|
||||
onChange: PropTypes.function.isRequired,
|
||||
dragHandleProps: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default Server;
|
||||
@@ -0,0 +1,136 @@
|
||||
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
|
||||
import Server from "./Server";
|
||||
import update from "immutability-helper";
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} monitors The server monitors options
|
||||
* @param {*} cards A set of servers/monitors user can add/remove/drag/drop to be displayed in status page
|
||||
* each card can be autocompleted/selected from the existing monitors list
|
||||
* @param {*} setCards Function to set the cards
|
||||
* @param {*} form The Status page form
|
||||
* @param {*} setForm Function to set the form
|
||||
* @param {*} removeItem The function used to remove a single server
|
||||
* @returns A list of user selected Servers/Monitors
|
||||
*/
|
||||
|
||||
|
||||
const ServersList = ({ monitors, cards, setCards, form, setForm, removeItem }) => {
|
||||
const grid = 8;
|
||||
|
||||
const handleCardChange = (event, val) => {
|
||||
let newCards;
|
||||
const { id } = event.target;
|
||||
let idx = cards.findIndex((a) => {
|
||||
let found = false;
|
||||
let optionIdx = id.indexOf("-option");
|
||||
if (optionIdx !== -1) found = a.id == id.substr(0, optionIdx);
|
||||
else found = a.id == id;
|
||||
return found;
|
||||
});
|
||||
|
||||
if (idx >= 0) {
|
||||
let x = JSON.parse(JSON.stringify(cards[idx]));
|
||||
x.val = val;
|
||||
newCards = update(cards, { $splice: [[idx, 1, x]] });
|
||||
} else {
|
||||
newCards = update(cards, { $push: [{ id: id, val: val }] });
|
||||
}
|
||||
setCards(newCards);
|
||||
setForm({ ...form, monitors: newCards.map((c) => c.val) });
|
||||
};
|
||||
|
||||
const moveCard = (dragIndex, hoverIndex) => {
|
||||
const dragCard = cards[dragIndex];
|
||||
const newCards = update(cards, {
|
||||
$splice: [
|
||||
[dragIndex, 1],
|
||||
[hoverIndex, 0, dragCard],
|
||||
],
|
||||
});
|
||||
setCards(newCards);
|
||||
setForm({ ...form, monitors: newCards.map((c) => c.val) });
|
||||
};
|
||||
|
||||
const handleDragEnd = (result) => {
|
||||
// dropped outside the list
|
||||
if (!result.destination) {
|
||||
return;
|
||||
}
|
||||
moveCard(result.source.index, result.destination.index);
|
||||
};
|
||||
|
||||
const getItemStyle = (isDragging, draggableStyle) => ({
|
||||
// some basic styles to make the items look a bit nicer
|
||||
userSelect: "none",
|
||||
padding: grid * 2,
|
||||
margin: `0 0 ${grid}px 0`,
|
||||
|
||||
// change background colour if dragging
|
||||
background: isDragging ? "lightgreen" : "lightgrey",
|
||||
|
||||
// styles we need to apply on draggables
|
||||
...draggableStyle,
|
||||
});
|
||||
|
||||
const getListStyle = (isDraggingOver) => ({
|
||||
background: isDraggingOver ? "lightblue" : "white",
|
||||
padding: grid,
|
||||
width: 550,
|
||||
});
|
||||
|
||||
return (
|
||||
<DragDropContext onDragEnd={handleDragEnd}>
|
||||
<Droppable droppableId="droppable">
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
{...provided.droppableProps}
|
||||
ref={provided.innerRef}
|
||||
style={getListStyle(snapshot.isDraggingOver)}
|
||||
>
|
||||
{cards.map((item, index) => (
|
||||
<Draggable
|
||||
key={item.id}
|
||||
draggableId={item.id}
|
||||
index={index}
|
||||
>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
style={getItemStyle(
|
||||
snapshot.isDragging,
|
||||
provided.draggableProps.style
|
||||
)}
|
||||
>
|
||||
<Server
|
||||
key={index}
|
||||
id={item?.id ?? "" + Math.random()}
|
||||
monitors={monitors}
|
||||
value={item.val ?? {}}
|
||||
onChange={handleCardChange}
|
||||
removeItem={removeItem}
|
||||
dragHandleProps={provided.dragHandleProps}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
);
|
||||
};
|
||||
|
||||
ServersList.propTypes = {
|
||||
monitors: PropTypes.array.isRequired,
|
||||
cards: PropTypes.array.isRequired,
|
||||
setCards: PropTypes.function.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
setForm: PropTypes.function.isRequired,
|
||||
removeItem: PropTypes.function.isRequired
|
||||
};
|
||||
|
||||
export default ServersList;
|
||||
@@ -6,8 +6,6 @@ import { Provider } from "react-redux";
|
||||
import { persistor, store } from "./store";
|
||||
import { PersistGate } from "redux-persist/integration/react";
|
||||
import NetworkServiceProvider from "./Utils/NetworkServiceProvider.jsx";
|
||||
import { DndProvider } from 'react-dnd'
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend'
|
||||
import { networkService } from "./Utils/NetworkService";
|
||||
export { networkService };
|
||||
|
||||
@@ -19,9 +17,7 @@ ReactDOM.createRoot(document.getElementById("root")).render(
|
||||
>
|
||||
<Router>
|
||||
<NetworkServiceProvider>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<App />
|
||||
</DndProvider>
|
||||
</NetworkServiceProvider>
|
||||
</Router>
|
||||
</PersistGate>
|
||||
|
||||
Reference in new issue
Block a user