Skip to content

Commit

Permalink
#175: added delete slice function on slice viewer page
Browse files Browse the repository at this point in the history
  • Loading branch information
yaxue1123 committed May 25, 2022
1 parent d897657 commit 345b1bb
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 93 deletions.
50 changes: 22 additions & 28 deletions src/components/SliceViewer/Graph.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,54 +15,48 @@ import { OverlayTrigger, Tooltip } from 'react-bootstrap';

Cytoscape.use(FCose);

function handleCy(cy) {
const SELECT_THRESHOLD = 200;

// Refresh Layout
const refreshLayout = _.debounce(() => {
const layout = {name: 'fcose', infinite: false};
cy.layout(layout).run()
}, SELECT_THRESHOLD);

// apply layout on graph render.
refreshLayout();

// refresh layout when elements change.
// cy.on('add remove', () => {
// refreshLayout();
// });

function setCytoscape(cy){
return cy;
}

export default class Graph extends Component {
state = {
w: 0,
h: 0,
selectedData: null,
}

componentDidMount = () => {
this.setState({
w: window.innerWidth * this.props.defaultSize.width,
h:window.innerHeight * this.props.defaultSize.height,
h: window.innerHeight * this.props.defaultSize.height,
})
// this.cy can only be declared after the component has been mounted
// call functions that set up the interactivity inside componentDidMount
this.setUpListeners();
}

setUpListeners = () => {
const SELECT_THRESHOLD = 200;

// Refresh Layout
const refreshLayout = _.debounce(() => {
const layout = {name: 'fcose', infinite: false};
this.cy.layout(layout).run()
}, SELECT_THRESHOLD);

// apply layout on graph render.
refreshLayout();

// refresh layout when elements change.
this.cy.on('add remove', () => {
refreshLayout();
});

// pass selected node data to parent component.
this.cy.on('click', 'node', (event) => {
this.setState({selectedData: event.target["_private"].data})
this.handleNodeSelect();
this.props.onNodeSelect(event.target["_private"].data);
});
}

handleNodeSelect = () => {
const selectedData = this.state.selectedData;
this.props.onNodeSelect(selectedData);
}

saveJSON = () => {
var jsonBlob = new Blob([ JSON.stringify( this.cy.json() ) ], { type: 'application/javascript;charset=utf-8' });
Expand Down Expand Up @@ -129,7 +123,7 @@ export default class Graph extends Component {
</OverlayTrigger>
}
{
this.props.isNewSlice &&
this.props.isNewSlice &&
<button onClick={this.props.onClearGraph} className="btn btn-sm btn-outline-danger">Clear Graph</button>
}
</div>
Expand All @@ -138,7 +132,7 @@ export default class Graph extends Component {
zoom={defaultSize.zoom}
pan={ { x: 350, y: 275 } }
style={{ width: this.state.w, height: this.state.h }}
cy={(cy) => {this.cy = handleCy(cy)}}
cy={(cy) => {this.cy = setCytoscape(cy)}}
stylesheet={[
{
"selector": "node",
Expand Down
156 changes: 91 additions & 65 deletions src/pages/SliceViewer.jsx
Original file line number Diff line number Diff line change
@@ -1,70 +1,82 @@
import React, { Component } from 'react'
import Graph from '../components/SliceViewer/Graph';
import DetailForm from '../components/SliceViewer/DetailForm';
import DeleteModal from "../components/common/DeleteModal";
import { Link } from "react-router-dom";
// import { autoCreateTokens, autoRefreshTokens } from "../utils/manageTokens";
// import { getCurrentUser } from "../services/prPeopleService.js";
// import { getSliceById } from "../services/orchestratorService.js";
import { getSliceById } from "../services/fakeSlices";
import { autoCreateTokens, autoRefreshTokens } from "../utils/manageTokens";
import { getCurrentUser } from "../services/prPeopleService.js";
import { getSliceById, deleteSlice } from "../services/orchestratorService.js";
// import { getSliceById } from "../services/fakeSlices";
import sliceParser from "../services/parser/sliceParser.js";
import sliceTimeParser from "../utils/sliceTimeParser.js";
import { toast } from "react-toastify";

export default class SliceViewer extends Component {
state = {
// elements: [],
// slice: {
// "graph_id": "",
// "lease_end": "",
// "slice_id": "",
// "slice_model": "",
// "slice_name": "Slice Viewer",
// "slice_state": "StableOK"
// },
elements: sliceParser(getSliceById(1)["value"]["slices"][0]["slice_model"]),
slice: getSliceById(1)["value"]["slices"][0],
elements: [],
slice: {
"graph_id": "",
"lease_end": "",
"slice_id": "",
"slice_model": "",
"slice_name": "Slice Viewer",
"slice_state": "StableOK"
},
// elements: sliceParser(getSliceById(1)["value"]["slices"][0]["slice_model"]),
// slice: getSliceById(1)["value"]["slices"][0],
selectedData: null,
positionAddNode: { x: 100, y: 600 },
hasProject: true,
}

// async componentDidMount() {
// // call PR first to check if the user has project.
// try {
// const { data: people } = await getCurrentUser();
// if (people.projects.length === 0) {
// this.setState({ hasProject: false });
// } else {
// // call credential manager to generate tokens
// // if nothing found in browser storage
// if (!localStorage.getItem("idToken") || !localStorage.getItem("refreshToken")) {
// autoCreateTokens(people.projects[0].uuid).then(async () => {
// const { data } = await getSliceById(this.props.match.params.id);
// this.setState({ elements: sliceParser(data["value"]["slices"][0]["slice_model"])});
// this.setState({ slice: data["value"]["slices"][0] });
// });
// } else {
// // the token has been stored in the browser and is ready to be used.
// try {
// const { data } = await getSliceById(this.props.match.params.id);
// this.setState({ elements: sliceParser(data["value"]["slices"][0]["slice_model"])});
// this.setState({ slice: data["value"]["slices"][0] });
// } catch(err) {
// console.log("Error in getting slice: " + err);
// toast.error("Failed to load the slice. Please try again later.");
// if (err.response.status === 401) {
// // 401 Error: Provided token is not valid.
// // refresh the token by calling credential manager refresh_token.
// autoRefreshTokens(people.projects[0].uuid);
// }
// }
// }
// }
// } catch(ex) {
// toast.error("Failed to load user information. Please reload this page.");
// console.log("Failed to load user information: " + ex.response.data);
// }
// }
async componentDidMount() {
// call PR first to check if the user has project.
try {
const { data: people } = await getCurrentUser();
if (people.projects.length === 0) {
this.setState({ hasProject: false });
} else {
// call credential manager to generate tokens
// if nothing found in browser storage
if (!localStorage.getItem("idToken") || !localStorage.getItem("refreshToken")) {
autoCreateTokens(people.projects[0].uuid).then(async () => {
const { data } = await getSliceById(this.props.match.params.id);
this.setState({ elements: sliceParser(data["value"]["slices"][0]["slice_model"])});
this.setState({ slice: data["value"]["slices"][0] });
});
} else {
// the token has been stored in the browser and is ready to be used.
try {
const { data } = await getSliceById(this.props.match.params.id);
this.setState({ elements: sliceParser(data["value"]["slices"][0]["slice_model"])});
this.setState({ slice: data["value"]["slices"][0] });
} catch(err) {
console.log("Error in getting slice: " + err);
toast.error("Failed to load the slice. Please try again later.");
if (err.response.status === 401) {
// 401 Error: Provided token is not valid.
// refresh the token by calling credential manager refresh_token.
autoRefreshTokens(people.projects[0].uuid);
}
}
}
}
} catch(ex) {
toast.error("Failed to load user information. Please reload this page.");
console.log("Failed to load user information: " + ex.response.data);
}
}

handleDeleteSlice = async (id) => {
try {
await deleteSlice(id);
// toast message to users when the api call is successfully done.
toast.success("Slice deleted successfully.");
} catch(ex) {
console.log("failed to delete the slice: " + ex.response.data);
toast.error("Failed to delete the slice.");
}
}

handleNodeSelect = (selectedData) => {
this.setState({ selectedData });
Expand All @@ -88,19 +100,33 @@ export default class SliceViewer extends Component {
hasProject &&
<div className="mx-5 mb-4 slice-viewer-container">
<div className="d-flex flex-row justify-content-between align-items-center mt-2">
<h2>
<b>{slice.slice_name}</b>
<span className={`badge badge-${stateColors[slice.slice_state]} ml-2`}>{slice.slice_state}</span>
</h2>
<u>Lease End: {sliceTimeParser(slice.lease_end)}</u>
<Link to="/experiments#slices">
<button
className="btn btn-sm btn-outline-primary my-3"
>
<i className="fa fa-sign-in mr-2"></i>
Back to Slice List
</button>
</Link>
<div className="d-flex flex-row justify-content-between align-items-center">
<h2 className="mr-4">
<b>{slice.slice_name}</b>
<span className={`badge badge-${stateColors[slice.slice_state]} ml-2`}>{slice.slice_state}</span>
</h2>
<h4>
<span className="badge badge-light font-weight-normal p-2 mt-1">Lease End Time: {sliceTimeParser(slice.lease_end)}</span>
</h4>
</div>
<div className="d-flex flex-row justify-content-between align-items-center">
{
slice.slice_state !== "Dead" &&
<DeleteModal
name={"Delete Slice"}
text={'Are you sure you want to delete this slice? This process cannot be undone but you can find deleted slices by checking the "Include Dead Slices" radio button on Experiments -> Slices page.'}
onDelete={() => this.handleDeleteSlice(slice.slice_id)}
/>
}
<Link to="/experiments#slices">
<button
className="btn btn-sm btn-outline-primary my-3 ml-3"
>
<i className="fa fa-sign-in mr-2"></i>
Back to Slice List
</button>
</Link>
</div>
</div>
<div className="d-flex flex-row justify-content-center">
{
Expand Down

0 comments on commit 345b1bb

Please sign in to comment.