Skip to content

Commit

Permalink
Feature/site state and signup update (#265)
Browse files Browse the repository at this point in the history
* #262: added new site state - Partial Maintenance

* #262: updated mock resource data to cover all site states

* #259: updated sign up step 1 wording

* #259: added the new sign up page for non-institutional idp users

* #259: updated the wording on new sign-up page

* #258: moved generate-key and upload-key components as child of key tabs

* #258: lifted the key limit check to parent KeyTabs component

* #258: updated style and wording

* #258: lifted key type as props passed to upload/generate key component

* #258: store key type after key generate/upload and page refresh

* #258: fixed local storage name issue

* #258: fixed the bug of not displaying generate/upload options when no key exists

* #258: fixed upload key parameter issue

* #179&#246&#256: wrapped up updates from another branch

* #246: pass time data to Calendar as Date format instead of string

* #264: changed the default tab of project as Basic Information

* #179: updated delete modal to include id property

* #246: moved lease end time and project to detail form to save space

* #258: wrapped up v1.4.7 updates

* #258: fixed an import issue
  • Loading branch information
yaxue1123 authored May 26, 2023
1 parent 20baa4b commit 5839631
Show file tree
Hide file tree
Showing 28 changed files with 451 additions and 239 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fabric-portal",
"version": "1.4.6",
"version": "1.4.7",
"private": true,
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.32",
Expand Down
24 changes: 10 additions & 14 deletions src/components/Experiment/Keys.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import React from "react";
import KeyTabs from "../SshKey/KeyTabs";
import GenerateKey from "../SshKey/GenerateKey";
import UploadKey from "../SshKey/UploadKey";
import { getActiveKeys } from "../../services/sshKeyService";

import { default as portalData } from "../../services/portalData.json";

import { toast } from "react-toastify";

class Keys extends React.Component {
state = {
keys: [],
keys: []
};

async componentDidMount() {
Expand All @@ -29,20 +24,21 @@ class Keys extends React.Component {
let sliverKeys = keys.filter(k => k.fabric_key_type === "sliver");
let bastionKeys = keys.filter(k => k.fabric_key_type === "bastion");

let maxSliver = sliverKeys.length >= portalData.sliverKeyLimit;
let maxBastion = bastionKeys.length >= portalData.bastionKeyLimit;

return { sliverKeys, bastionKeys, maxSliver, maxBastion };
return { sliverKeys, bastionKeys};
};

render() {
const { sliverKeys, bastionKeys, maxSliver, maxBastion } = this.getKeysData();
const { sliverKeys, bastionKeys } = this.getKeysData();

return (
<div className="col-9" id="sshKeys">
<KeyTabs sliverKeys={sliverKeys} bastionKeys={bastionKeys} disableKeyDelete={false} styleProp={"w-100"} parent={"Keys"}/>
<GenerateKey maxSliver={maxSliver} maxBastion={maxBastion}/>
<UploadKey maxSliver={maxSliver} maxBastion={maxBastion}/>
<KeyTabs
sliverKeys={sliverKeys}
bastionKeys={bastionKeys}
disableKeyDelete={false}
styleProp={"w-100"}
parent={"Keys"}
/>
</div>
);
}
Expand Down
44 changes: 36 additions & 8 deletions src/components/Experiment/Slices.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import Pagination from "../common/Pagination";
import SearchBoxWithDropdown from "../../components/common/SearchBoxWithDropdown";
import SlicesTable from "../Slice/SlicesTable";
import SpinnerWithText from "../../components/common/SpinnerWithText";
// import DeleteModal from "../../components/common/DeleteModal";
import { getProjects } from "../../services/projectService.js";
import { autoCreateTokens } from "../../utils/manageTokens";
import { getSlices } from "../../services/sliceService.js";
import { getSlices, deleteSlice } from "../../services/sliceService.js";
import { toast } from "react-toastify";
import paginate from "../../utils/paginate";
import sleep from "../../utils/sleep";
import checkPortalType from "../../utils/checkPortalType";
import { default as portalData } from "../../services/portalData.json";
import _ from "lodash";
Expand All @@ -31,29 +33,30 @@ class Slices extends React.Component {
searchQuery: "",
sortColumn: { path: "name", order: "asc" },
showSpinner: false,
spinnerText: ""
};

async componentDidMount() {
// Show loading spinner and when waiting API response
this.setState({ showSpinner: true });
this.setState({ showSpinner: true, spinnerText: "Loading slices..." });
try {
if (window.location.href.includes("/projects")) {
// call credential manager to generate tokens
autoCreateTokens("all").then(async () => {
const { data: res } = await getSlices();
const slices = res.data.filter(s => s.project_id === this.props.projectId);
this.setState({ slices, showSpinner: false });
this.setState({ slices, showSpinner: false, spinnerText: "" });
});
} else {
// call PR first to check if the user has project.
const { data: res } = await getProjects("myProjects", 0, 200);
if (res.results.length === 0) {
this.setState({ hasProject: false, showSpinner: false });
this.setState({ hasProject: false, showSpinner: false, spinnerText: "" });
} else {
// call credential manager to generate tokens
autoCreateTokens("all").then(async () => {
const { data: res } = await getSlices();
this.setState({ slices: res.data, showSpinner: false });
this.setState({ slices: res.data, showSpinner: false, spinnerText: "" });
});
}
}
Expand Down Expand Up @@ -83,6 +86,21 @@ class Slices extends React.Component {
this.setState( { includeDeadSlices: !currentChoice });
}

handleDeleteAllSlices = async () => {
try {
this.setState({
showSpinner: true,
spinnerText: "Deleting all active slices..."
})
await deleteSlice();
await sleep(5000);
window.location.reload();
}
catch (err) {
toast.error("Failed to delete all slices of this project.")
}
}

getPageData = () => {
const {
pageSize,
Expand Down Expand Up @@ -121,7 +139,8 @@ class Slices extends React.Component {
};

render() {
const { hasProject, slices, pageSize, currentPage, sortColumn, searchQuery, filterQuery, showSpinner } = this.state;
const { hasProject, slices, pageSize, currentPage, sortColumn, searchQuery,
filterQuery, showSpinner, spinnerText, includeDeadSlices } = this.state;
const { totalCount, data } = this.getPageData();

return (
Expand Down Expand Up @@ -149,7 +168,7 @@ class Slices extends React.Component {
</div>
}
{
showSpinner && <SpinnerWithText text={"Loading slices..."} />
showSpinner && <SpinnerWithText text={spinnerText} />
}
{
!showSpinner && !hasProject &&
Expand Down Expand Up @@ -254,9 +273,18 @@ class Slices extends React.Component {
<Checkbox
label={"Include Dead/ Closing Slices"}
id={"checkbox-include-dead-slices"}
isChecked={this.state.includeDeadSlices}
isChecked={includeDeadSlices}
onCheck={this.handleIncludeDeadSlices}
/>
{/* {
this.props.parent === "Projects" && totalCount > 0 && !includeDeadSlices &&
<DeleteModal
name={"Delete All"}
text={"Are you sure you want to delete all the active slices? This process cannot be undone."}
id={"delete-all-slices"}
onDelete={() => this.handleDeleteAllSlices()}
/>
} */}
</div>
<SlicesTable
slices={data}
Expand Down
1 change: 1 addition & 0 deletions src/components/Project/ProjectBasicInfoTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class ProjectBasicInfoTable extends Component {
<DeleteModal
name={"Delete Project"}
text={"Are you sure you want to delete the project? This process cannot be undone."}
id={"delete-project"}
onDelete={() => onDeleteProject(project)}
/>
</td>
Expand Down
6 changes: 6 additions & 0 deletions src/components/Resource/DetailTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ const DetailTable = props => {
colorHex: "#ffb670",
labelColorHex: "#212529"
},
"PartMaint": {
state: "Partial Maintenance",
colorName: "info",
colorHex: "#ffb670",
labelColorHex: "#212529"
},
"Active": {
state: "Active",
colorName: "primary",
Expand Down
11 changes: 7 additions & 4 deletions src/components/Resource/SiteDetailPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ const SiteDetailPage = props => {
color: "warning",
explanation: "requests will be expected until the deadline"
},
"PartMaint": {
state: "Partial Maintenance",
colorHex: "warning",
explanation: "requests may fail due to some equipment being off-line"
},
"Active": {
state: "Active",
color: "primary",
explanation: ""

}
}

Expand All @@ -43,7 +47,7 @@ const SiteDetailPage = props => {
</Link>
</div>
{
["Maint", "PreMaint"].includes(data.status["state"]) &&
["Maint", "PreMaint", "PartMaint"].includes(data.status["state"]) &&
<div className="alert alert-primary mb-2" role="alert">
<i className="fa fa-exclamation-triangle mr-2"></i>
Please check the <i className="fa fa-sign-in ml-1 mr-2"></i>
Expand Down Expand Up @@ -95,8 +99,7 @@ const SiteDetailPage = props => {
<th>Deadline</th>
<td>
{
data.status["deadline"] ?
utcToLocalTimeParser(data.status["deadline"]) : "Unknown"
data.status["deadline"] ? data.status["deadline"] : "Unknown"
}
</td>
</tr>
Expand Down
8 changes: 8 additions & 0 deletions src/components/Resource/SummaryTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ class SummaryTable extends Component {
</span>
</div>
}
{
resource.status && resource.status.state === "PartMaint" &&
<div>
<span className="badge badge-pill badge-info px-2">
Partial Maintenance
</span>
</div>
}
</div>
),
label: "Site",
Expand Down
30 changes: 30 additions & 0 deletions src/components/Signup/DenyEnrollment.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from "react";
import { default as portalData } from "../../services/portalData.json";

const DenyEnrollment = () => {
return (
<div>
<div>
<p>Dear Experimenter,</p>
<p>FABRIC does not support GitHub, Google, Microsoft or ORCID as an identity provider for enrollment without a prior agreement.</p>
<p>
If your institution is not listed among the Identity Providers supported by CILogon or you are not able to use it for some other reason, you may initiate a petition to be added via a non-institutional Identity Provider, such as GitHub, Google, Microsoft or ORCID. Please note that such petitions undergo manual review - we may request additional information prior to granting access.
</p>
</div>
<div className="text-center">
<div className="alert alert-warning" role="alert">
<i className="fa fa-exclamation-triangle mr-2"></i> Please note that the FABRIC team works standard business hours in US Eastern timezone. We generally do not handle requests on weekends or during holidays.
</div>
<a href={portalData.jiraLinks["initiateAccountPetition"]}>
<button
className="btn btn-primary mt-2"
>
Initiate Petition
</button>
</a>
</div>
</div>
)
}

export default DenyEnrollment;
2 changes: 1 addition & 1 deletion src/components/Signup/Step1.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const Step1 = () => {
</div>
<div className="text-center">
<div className="alert alert-warning" role="alert">
<i className="fa fa-exclamation-triangle mr-2"></i> Please <b>choose your institution</b> and <b> NOT use the ORCID</b> option from the CILogon page.
<i className="fa fa-exclamation-triangle mr-2"></i> Please <b>choose your institution</b> and <b>NOT</b> use the GitHub, Google, Microsoft, or ORCID options from the CILogon page.
<br></br>(<b>Note</b>: the list may take a moment to populate beyond the default of ORCID).
</div>
<a href={portalData.selfEnrollRequest.links[checkPortalType(window.location.href)]}>
Expand Down
66 changes: 44 additions & 22 deletions src/components/SliceViewer/DetailForm.jsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,19 @@
import React, { Component } from 'react'
import CopyButton from "../common/CopyButton";
// import { Link } from "react-router-dom";
import { default as portalData } from "../../services/portalData.json";
// import utcToLocalTimeParser from "../../utils/utcToLocalTimeParser.js";
// import Calendar from "../../components/common/Calendar";

export default class DetailForm extends Component {
sshCommand = (managementIp, imageRef) => {
const usernameOnImageMapping = {
"default_centos8_stream": "centos",
"default_centos9_stream": "centos",
"default_centos_7": "centos",
"default_centos_8": "centos",
"default_debian_10": "debian",
"default_fedora_35": "fedora",
"default_rocky_8": "rocky",
"default_ubuntu_18": "ubuntu",
"default_ubuntu_20": "ubuntu",
"default_ubuntu_21": "ubuntu",
"default_ubuntu_22": "ubuntu",
"default_debian_11": "debian",
"default_fedora_36": "fedora",
"default_fedora_37": "fedora",
"docker_rocky_8": "rocky",
"docker_ubuntu_20": "ubuntu",
"docker_ubuntu_22": "ubuntu"
}
const usernameBasedOnImage=usernameOnImageMapping[imageRef.split(",")[0]];
const usernameBasedOnImage= portalData.usernameOnImageMapping[imageRef.split(",")[0]];
return `ssh -F <path to SSH config file> -i <path to private sliver key> ${usernameBasedOnImage}@${managementIp}`
}

render() {
const data = this.props.data;
// const { slice, data, leaseEndTime } = this.props;
const { data } = this.props;
return (
<div className="w-100 card ml-4">
<form>
Expand All @@ -39,7 +24,38 @@ export default class DetailForm extends Component {
<div className="form-col px-3">
{
!data && (
<span> Click an element to view details. </span>
<div>
<span>Click an element to view details. </span>
{/* {
slice.project_name && <div className="row mb-2">
<label>Project</label>
<Link to={`/projects/${slice.project_id}`}>{slice.project_name}</Link>
</div>
}
<div className="row mb-2">
<label>Lease End at</label>
{
slice.state !=="StableOK" && utcToLocalTimeParser(leaseEndTime)
}
{
leaseEndTime !== "" && slice.state ==="StableOK" && <Calendar
id="sliceViewerCalendar"
name="sliceViewerCalendar"
currentTime={new Date(utcToLocalTimeParser(leaseEndTime))}
onTimeChange={this.props.onTimeChange}
/>
}
{
slice.state ==="StableOK" &&
<button
className="btn btn-sm btn-outline-primary m1-3 mr-3"
onClick={this.props.onSliceExtend}
>
Extend
</button>
}
</div> */}
</div>
)
}

Expand Down Expand Up @@ -72,6 +88,12 @@ export default class DetailForm extends Component {
href={`${portalData.learnArticles.guideToLoginToFabricVMs}#project-permissions`}
target="_blank" rel="noreferrer" className="ml-1">
<i className="fa fa-question-circle mx-2"></i>
{/* <button
className="btn btn-sm btn-outline-primary ml-2"
onClick={() => this.props.openModalForm()}
>
Open Terminal
</button> */}
</a>
</label>
<div className="ssh-command">
Expand Down
5 changes: 5 additions & 0 deletions src/components/SliceViewer/SideNodes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ class SideNodes extends React.Component {
Pre-Maintenance
</span>
}
{
selectedSite.status.state === "PartMaint" && <span className="badge badge-pill badge-info px-2">
Partial Maintenance
</span>
}
</div>
<SiteResourceTable site={selectedSite} />
</div>
Expand Down
Loading

0 comments on commit 5839631

Please sign in to comment.