summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-ui/src/components/projects
diff options
context:
space:
mode:
Diffstat (limited to 'opendc-web/opendc-web-ui/src/components/projects')
-rw-r--r--opendc-web/opendc-web-ui/src/components/projects/FilterPanel.js26
-rw-r--r--opendc-web/opendc-web-ui/src/components/projects/FilterPanel.module.css7
-rw-r--r--opendc-web/opendc-web-ui/src/components/projects/NewPortfolio.js53
-rw-r--r--opendc-web/opendc-web-ui/src/components/projects/NewPortfolioModal.js161
-rw-r--r--opendc-web/opendc-web-ui/src/components/projects/NewTopology.js57
-rw-r--r--opendc-web/opendc-web-ui/src/components/projects/NewTopologyModal.js115
-rw-r--r--opendc-web/opendc-web-ui/src/components/projects/PortfolioTable.js99
-rw-r--r--opendc-web/opendc-web-ui/src/components/projects/ProjectCollection.js137
-rw-r--r--opendc-web/opendc-web-ui/src/components/projects/ProjectOverview.js98
-rw-r--r--opendc-web/opendc-web-ui/src/components/projects/TopologyTable.js115
10 files changed, 0 insertions, 868 deletions
diff --git a/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.js b/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.js
deleted file mode 100644
index 5aaa56ac..00000000
--- a/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import { ToggleGroup, ToggleGroupItem } from '@patternfly/react-core'
-import { filterPanel } from './FilterPanel.module.css'
-
-export const FILTERS = { SHOW_ALL: 'All Projects', SHOW_OWN: 'My Projects', SHOW_SHARED: 'Shared with me' }
-
-const FilterPanel = ({ onSelect, activeFilter = 'SHOW_ALL' }) => (
- <ToggleGroup className={`${filterPanel} pf-u-mb-sm`}>
- {Object.keys(FILTERS).map((filter) => (
- <ToggleGroupItem
- key={filter}
- onChange={() => activeFilter === filter || onSelect(filter)}
- isSelected={activeFilter === filter}
- text={FILTERS[filter]}
- />
- ))}
- </ToggleGroup>
-)
-
-FilterPanel.propTypes = {
- onSelect: PropTypes.func.isRequired,
- activeFilter: PropTypes.string,
-}
-
-export default FilterPanel
diff --git a/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.module.css b/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.module.css
deleted file mode 100644
index 15c36821..00000000
--- a/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.module.css
+++ /dev/null
@@ -1,7 +0,0 @@
-.filterPanel {
- display: flex;
-}
-
-.filterPanel > button {
- flex: 1 !important;
-}
diff --git a/opendc-web/opendc-web-ui/src/components/projects/NewPortfolio.js b/opendc-web/opendc-web-ui/src/components/projects/NewPortfolio.js
deleted file mode 100644
index aebcc3c9..00000000
--- a/opendc-web/opendc-web-ui/src/components/projects/NewPortfolio.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2021 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-import PropTypes from 'prop-types'
-import { PlusIcon } from '@patternfly/react-icons'
-import { Button } from '@patternfly/react-core'
-import { useState } from 'react'
-import { useNewPortfolio } from '../../data/project'
-import NewPortfolioModal from './NewPortfolioModal'
-
-function NewPortfolio({ projectId }) {
- const [isVisible, setVisible] = useState(false)
- const { mutate: addPortfolio } = useNewPortfolio()
-
- const onSubmit = (name, targets) => {
- addPortfolio({ projectId, name, targets })
- setVisible(false)
- }
-
- return (
- <>
- <Button icon={<PlusIcon />} isSmall onClick={() => setVisible(true)}>
- New Portfolio
- </Button>
- <NewPortfolioModal isOpen={isVisible} onSubmit={onSubmit} onCancel={() => setVisible(false)} />
- </>
- )
-}
-
-NewPortfolio.propTypes = {
- projectId: PropTypes.number,
-}
-
-export default NewPortfolio
diff --git a/opendc-web/opendc-web-ui/src/components/projects/NewPortfolioModal.js b/opendc-web/opendc-web-ui/src/components/projects/NewPortfolioModal.js
deleted file mode 100644
index ba4bc819..00000000
--- a/opendc-web/opendc-web-ui/src/components/projects/NewPortfolioModal.js
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (c) 2021 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-import PropTypes from 'prop-types'
-import React, { useRef, useState } from 'react'
-import {
- Form,
- FormGroup,
- FormSection,
- NumberInput,
- Select,
- SelectGroup,
- SelectOption,
- SelectVariant,
- TextInput,
-} from '@patternfly/react-core'
-import Modal from '../util/modals/Modal'
-import { METRIC_GROUPS, METRIC_NAMES } from '../../util/available-metrics'
-
-const NewPortfolioModal = ({ isOpen, onSubmit: onSubmitUpstream, onCancel: onUpstreamCancel }) => {
- const nameInput = useRef(null)
- const [repeats, setRepeats] = useState(1)
- const [isSelectOpen, setSelectOpen] = useState(false)
- const [selectedMetrics, setSelectedMetrics] = useState([])
-
- const [isSubmitted, setSubmitted] = useState(false)
- const [errors, setErrors] = useState({})
-
- const clearState = () => {
- setSubmitted(false)
- setErrors({})
- nameInput.current.value = ''
- setRepeats(1)
- setSelectOpen(false)
- setSelectedMetrics([])
- }
-
- const onSubmit = (event) => {
- setSubmitted(true)
-
- if (event) {
- event.preventDefault()
- }
-
- const name = nameInput.current.value
-
- if (!name) {
- setErrors({ name: true })
- return false
- } else {
- onSubmitUpstream(name, { metrics: selectedMetrics, repeats })
- }
-
- clearState()
- return false
- }
- const onCancel = () => {
- onUpstreamCancel()
- clearState()
- }
-
- const onSelect = (event, selection) => {
- if (selectedMetrics.includes(selection)) {
- setSelectedMetrics((metrics) => metrics.filter((item) => item !== selection))
- } else {
- setSelectedMetrics((metrics) => [...metrics, selection])
- }
- }
-
- return (
- <Modal title="New Portfolio" isOpen={isOpen} onSubmit={onSubmit} onCancel={onCancel}>
- <Form onSubmit={onSubmit}>
- <FormSection>
- <FormGroup
- label="Name"
- fieldId="name"
- isRequired
- validated={isSubmitted && errors.name ? 'error' : 'default'}
- helperTextInvalid="This field cannot be empty"
- >
- <TextInput
- name="name"
- id="name"
- type="text"
- isRequired
- ref={nameInput}
- placeholder="My Portfolio"
- />
- </FormGroup>
- </FormSection>
- <FormSection title="Targets" titleElement="h4">
- <FormGroup label="Metrics" fieldId="metrics">
- <Select
- variant={SelectVariant.typeaheadMulti}
- typeAheadAriaLabel="Select a metric"
- onToggle={() => setSelectOpen(!isSelectOpen)}
- onSelect={onSelect}
- onClear={() => setSelectedMetrics([])}
- selections={selectedMetrics}
- isOpen={isSelectOpen}
- placeholderText="Select a metric"
- menuAppendTo="parent"
- maxHeight="300px"
- chipGroupProps={{ numChips: 1 }}
- isGrouped
- >
- {Object.entries(METRIC_GROUPS).map(([group, metrics]) => (
- <SelectGroup label={group} key={group}>
- {metrics.map((metric) => (
- <SelectOption key={metric} value={metric}>
- {METRIC_NAMES[metric]}
- </SelectOption>
- ))}
- </SelectGroup>
- ))}
- </Select>
- </FormGroup>
- <FormGroup label="Repeats per Scenario" fieldId="repeats">
- <NumberInput
- id="repeats"
- inputName="repeats"
- type="number"
- value={repeats}
- onChange={(e) => setRepeats(Number(e.target.value))}
- onPlus={() => setRepeats((r) => r + 1)}
- onMinus={() => setRepeats((r) => r - 1)}
- min={1}
- />
- </FormGroup>
- </FormSection>
- </Form>
- </Modal>
- )
-}
-
-NewPortfolioModal.propTypes = {
- isOpen: PropTypes.bool.isRequired,
- onSubmit: PropTypes.func.isRequired,
- onCancel: PropTypes.func.isRequired,
-}
-
-export default NewPortfolioModal
diff --git a/opendc-web/opendc-web-ui/src/components/projects/NewTopology.js b/opendc-web/opendc-web-ui/src/components/projects/NewTopology.js
deleted file mode 100644
index 4c569c56..00000000
--- a/opendc-web/opendc-web-ui/src/components/projects/NewTopology.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2021 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-import PropTypes from 'prop-types'
-import { PlusIcon } from '@patternfly/react-icons'
-import { Button } from '@patternfly/react-core'
-import { useState } from 'react'
-import { useNewTopology } from '../../data/topology'
-import NewTopologyModal from './NewTopologyModal'
-
-function NewTopology({ projectId }) {
- const [isVisible, setVisible] = useState(false)
- const { mutate: addTopology } = useNewTopology()
-
- const onSubmit = (topology) => {
- addTopology(topology)
- setVisible(false)
- }
- return (
- <>
- <Button icon={<PlusIcon />} isSmall onClick={() => setVisible(true)}>
- New Topology
- </Button>
- <NewTopologyModal
- projectId={projectId}
- isOpen={isVisible}
- onSubmit={onSubmit}
- onCancel={() => setVisible(false)}
- />
- </>
- )
-}
-
-NewTopology.propTypes = {
- projectId: PropTypes.number,
-}
-
-export default NewTopology
diff --git a/opendc-web/opendc-web-ui/src/components/projects/NewTopologyModal.js b/opendc-web/opendc-web-ui/src/components/projects/NewTopologyModal.js
deleted file mode 100644
index 780ec034..00000000
--- a/opendc-web/opendc-web-ui/src/components/projects/NewTopologyModal.js
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (c) 2021 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-import produce from 'immer'
-import PropTypes from 'prop-types'
-import React, { useRef, useState } from 'react'
-import { Form, FormGroup, FormSelect, FormSelectOption, TextInput } from '@patternfly/react-core'
-import { useTopologies } from '../../data/topology'
-import Modal from '../util/modals/Modal'
-
-const NewTopologyModal = ({ projectId, isOpen, onSubmit: onSubmitUpstream, onCancel: onCancelUpstream }) => {
- const nameInput = useRef(null)
- const [isSubmitted, setSubmitted] = useState(false)
- const [originTopology, setOriginTopology] = useState(-1)
- const [errors, setErrors] = useState({})
-
- const { data: topologies = [] } = useTopologies(projectId, { enabled: isOpen })
-
- const clearState = () => {
- if (nameInput.current) {
- nameInput.current.value = ''
- }
- setSubmitted(false)
- setOriginTopology(-1)
- setErrors({})
- }
-
- const onSubmit = (event) => {
- setSubmitted(true)
-
- if (event) {
- event.preventDefault()
- }
-
- const name = nameInput.current.value
-
- if (!name) {
- setErrors({ name: true })
- return false
- } else {
- const candidate = topologies.find((topology) => topology.id === originTopology) || { rooms: [] }
- const topology = produce(candidate, (draft) => {
- delete draft.project
- draft.projectId = projectId
- draft.name = name
- })
- onSubmitUpstream(topology)
- }
-
- clearState()
- return true
- }
-
- const onCancel = () => {
- onCancelUpstream()
- clearState()
- }
-
- return (
- <Modal title="New Topology" isOpen={isOpen} onSubmit={onSubmit} onCancel={onCancel}>
- <Form onSubmit={onSubmit}>
- <FormGroup
- label="Name"
- fieldId="name"
- isRequired
- validated={isSubmitted && errors.name ? 'error' : 'default'}
- helperTextInvalid="This field cannot be empty"
- >
- <TextInput id="name" name="name" type="text" isRequired ref={nameInput} />
- </FormGroup>
- <FormGroup label="Topology to duplicate" fieldId="origin" isRequired>
- <FormSelect
- id="origin"
- name="origin"
- value={originTopology}
- onChange={(v) => setOriginTopology(+v)}
- >
- <FormSelectOption value={-1} key={-1} label="None - start from scratch" />
- {topologies.map((topology) => (
- <FormSelectOption value={topology.id} key={topology.id} label={topology.name} />
- ))}
- </FormSelect>
- </FormGroup>
- </Form>
- </Modal>
- )
-}
-
-NewTopologyModal.propTypes = {
- projectId: PropTypes.number,
- isOpen: PropTypes.bool.isRequired,
- onSubmit: PropTypes.func.isRequired,
- onCancel: PropTypes.func.isRequired,
-}
-
-export default NewTopologyModal
diff --git a/opendc-web/opendc-web-ui/src/components/projects/PortfolioTable.js b/opendc-web/opendc-web-ui/src/components/projects/PortfolioTable.js
deleted file mode 100644
index 0afeaeaf..00000000
--- a/opendc-web/opendc-web-ui/src/components/projects/PortfolioTable.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (c) 2021 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-import { Bullseye } from '@patternfly/react-core'
-import PropTypes from 'prop-types'
-import Link from 'next/link'
-import { TableComposable, Thead, Tbody, Tr, Th, Td, ActionsColumn } from '@patternfly/react-table'
-import React from 'react'
-import TableEmptyState from '../util/TableEmptyState'
-import { usePortfolios, useDeletePortfolio } from '../../data/project'
-
-function PortfolioTable({ projectId }) {
- const { status, data: portfolios = [] } = usePortfolios(projectId)
- const { mutate: deletePortfolio } = useDeletePortfolio()
-
- const actions = (portfolio) => [
- {
- title: 'Delete Portfolio',
- onClick: () => deletePortfolio({ projectId, number: portfolio.number }),
- },
- ]
-
- return (
- <TableComposable aria-label="Portfolio List" variant="compact">
- <Thead>
- <Tr>
- <Th>Name</Th>
- <Th>Scenarios</Th>
- <Th>Metrics</Th>
- <Th>Repeats</Th>
- </Tr>
- </Thead>
- <Tbody>
- {portfolios.map((portfolio) => (
- <Tr key={portfolio.id}>
- <Td dataLabel="Name">
- <Link href={`/projects/${projectId}/portfolios/${portfolio.number}`}>{portfolio.name}</Link>
- </Td>
- <Td dataLabel="Scenarios">
- {portfolio.scenarios.length === 1
- ? '1 scenario'
- : `${portfolio.scenarios.length} scenarios`}
- </Td>
- <Td dataLabel="Metrics">
- {portfolio.targets.metrics.length === 1
- ? '1 metric'
- : `${portfolio.targets.metrics.length} metrics`}
- </Td>
- <Td dataLabel="Repeats">
- {portfolio.targets.repeats === 1 ? '1 repeat' : `${portfolio.targets.repeats} repeats`}
- </Td>
- <Td isActionCell>
- <ActionsColumn items={actions(portfolio)} />
- </Td>
- </Tr>
- ))}
- {portfolios.length === 0 && (
- <Tr>
- <Td colSpan={4}>
- <Bullseye>
- <TableEmptyState
- status={status}
- loadingTitle="Loading portfolios"
- emptyTitle="No portfolios"
- emptyText="You have not created any portfolio for this project yet. Click the New Portfolio button to create one."
- />
- </Bullseye>
- </Td>
- </Tr>
- )}
- </Tbody>
- </TableComposable>
- )
-}
-
-PortfolioTable.propTypes = {
- projectId: PropTypes.number,
-}
-
-export default PortfolioTable
diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectCollection.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectCollection.js
deleted file mode 100644
index a26fed46..00000000
--- a/opendc-web/opendc-web-ui/src/components/projects/ProjectCollection.js
+++ /dev/null
@@ -1,137 +0,0 @@
-import Link from 'next/link'
-import {
- Gallery,
- Bullseye,
- EmptyState,
- EmptyStateIcon,
- Card,
- CardTitle,
- CardActions,
- DropdownItem,
- CardHeader,
- Dropdown,
- KebabToggle,
- CardBody,
- CardHeaderMain,
- TextVariants,
- Text,
- TextContent,
- Tooltip,
- Button,
- Label,
-} from '@patternfly/react-core'
-import { PlusIcon, FolderIcon, TrashIcon } from '@patternfly/react-icons'
-import PropTypes from 'prop-types'
-import React, { useReducer, useMemo } from 'react'
-import { Project, Status } from '../../shapes'
-import { parseAndFormatDateTime } from '../../util/date-time'
-import { AUTH_DESCRIPTION_MAP, AUTH_ICON_MAP, AUTH_NAME_MAP } from '../../util/authorizations'
-import TableEmptyState from '../util/TableEmptyState'
-
-function ProjectCard({ project, onDelete }) {
- const [isKebabOpen, toggleKebab] = useReducer((t) => !t, false)
- const { id, role, name, updatedAt } = project
- const Icon = AUTH_ICON_MAP[role]
-
- return (
- <Card
- isCompact
- isRounded
- isFlat
- className="pf-u-min-height"
- style={{ '--pf-u-min-height--MinHeight': '175px' }}
- >
- <CardHeader className="pf-u-flex-grow-1">
- <CardHeaderMain className="pf-u-align-self-flex-start">
- <FolderIcon />
- </CardHeaderMain>
- <CardActions>
- <Tooltip content={AUTH_DESCRIPTION_MAP[role]}>
- <Label icon={<Icon />}>{AUTH_NAME_MAP[role]}</Label>
- </Tooltip>
- <Dropdown
- isPlain
- position="right"
- toggle={<KebabToggle className="pf-u-px-0" onToggle={toggleKebab} />}
- isOpen={isKebabOpen}
- dropdownItems={[
- <DropdownItem
- key="trash"
- onClick={() => {
- onDelete()
- toggleKebab()
- }}
- position="right"
- icon={<TrashIcon />}
- >
- Delete
- </DropdownItem>,
- ]}
- />
- </CardActions>
- </CardHeader>
- <CardTitle component={Link} className="pf-u-pb-0" href={`/projects/${id}`}>
- {name}
- </CardTitle>
- <CardBody isFilled={false}>
- <TextContent>
- <Text component={TextVariants.small}>Last modified {parseAndFormatDateTime(updatedAt)}</Text>
- </TextContent>
- </CardBody>
- </Card>
- )
-}
-
-function ProjectCollection({ status, projects, onDelete, onCreate, isFiltering }) {
- const sortedProjects = useMemo(() => {
- const res = [...projects]
- res.sort((a, b) => (new Date(a.updatedAt) < new Date(b.updatedAt) ? 1 : -1))
- return res
- }, [projects])
-
- if (sortedProjects.length === 0) {
- return (
- <TableEmptyState
- status={status}
- isFiltering={isFiltering}
- loadingTitle="Loading Projects"
- emptyTitle="No projects"
- emptyText="You have not created any projects yet. Create a new project to get started quickly."
- emptyAction={
- <Button icon={<PlusIcon />} onClick={onCreate}>
- Create Project
- </Button>
- }
- />
- )
- }
-
- return (
- <Gallery hasGutter aria-label="Available projects">
- {sortedProjects.map((project) => (
- <ProjectCard key={project.id} project={project} onDelete={() => onDelete(project)} />
- ))}
- <Card isCompact isFlat isRounded style={{ borderStyle: 'dotted' }}>
- <Bullseye>
- <EmptyState>
- <Button isBlock variant="link" onClick={onCreate}>
- <EmptyStateIcon icon={PlusIcon} />
- <br />
- Create Project
- </Button>
- </EmptyState>
- </Bullseye>
- </Card>
- </Gallery>
- )
-}
-
-ProjectCollection.propTypes = {
- status: Status.isRequired,
- isFiltering: PropTypes.bool,
- projects: PropTypes.arrayOf(Project).isRequired,
- onDelete: PropTypes.func,
- onCreate: PropTypes.func,
-}
-
-export default ProjectCollection
diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectOverview.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectOverview.js
deleted file mode 100644
index 3e1656f6..00000000
--- a/opendc-web/opendc-web-ui/src/components/projects/ProjectOverview.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (c) 2021 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-import PropTypes from 'prop-types'
-import {
- Card,
- CardActions,
- CardBody,
- CardHeader,
- CardTitle,
- DescriptionList,
- DescriptionListDescription,
- DescriptionListGroup,
- DescriptionListTerm,
- Grid,
- GridItem,
- Skeleton,
-} from '@patternfly/react-core'
-import NewTopology from './NewTopology'
-import TopologyTable from './TopologyTable'
-import NewPortfolio from './NewPortfolio'
-import PortfolioTable from './PortfolioTable'
-import { useProject } from '../../data/project'
-
-function ProjectOverview({ projectId }) {
- const { data: project } = useProject(projectId)
-
- return (
- <Grid hasGutter>
- <GridItem md={2}>
- <Card>
- <CardTitle>Details</CardTitle>
- <CardBody>
- <DescriptionList>
- <DescriptionListGroup>
- <DescriptionListTerm>Name</DescriptionListTerm>
- <DescriptionListDescription>
- {project?.name ?? <Skeleton screenreaderText="Loading project" />}
- </DescriptionListDescription>
- </DescriptionListGroup>
- </DescriptionList>
- </CardBody>
- </Card>
- </GridItem>
- <GridItem md={5}>
- <Card>
- <CardHeader>
- <CardActions>
- <NewTopology projectId={projectId} />
- </CardActions>
- <CardTitle>Topologies</CardTitle>
- </CardHeader>
- <CardBody>
- <TopologyTable projectId={projectId} />
- </CardBody>
- </Card>
- </GridItem>
- <GridItem md={5}>
- <Card>
- <CardHeader>
- <CardActions>
- <NewPortfolio projectId={projectId} />
- </CardActions>
- <CardTitle>Portfolios</CardTitle>
- </CardHeader>
- <CardBody>
- <PortfolioTable projectId={projectId} />
- </CardBody>
- </Card>
- </GridItem>
- </Grid>
- )
-}
-
-ProjectOverview.propTypes = {
- projectId: PropTypes.number,
-}
-
-export default ProjectOverview
diff --git a/opendc-web/opendc-web-ui/src/components/projects/TopologyTable.js b/opendc-web/opendc-web-ui/src/components/projects/TopologyTable.js
deleted file mode 100644
index 1c2c4f04..00000000
--- a/opendc-web/opendc-web-ui/src/components/projects/TopologyTable.js
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (c) 2021 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-import { Bullseye, AlertGroup, Alert, AlertVariant, AlertActionCloseButton } from '@patternfly/react-core'
-import PropTypes from 'prop-types'
-import Link from 'next/link'
-import { Tr, Th, Thead, Td, ActionsColumn, Tbody, TableComposable } from '@patternfly/react-table'
-import React, { useState } from 'react'
-import TableEmptyState from '../util/TableEmptyState'
-import { parseAndFormatDateTime } from '../../util/date-time'
-import { useTopologies, useDeleteTopology } from '../../data/topology'
-
-function TopologyTable({ projectId }) {
- const [error, setError] = useState('')
-
- const { status, data: topologies = [] } = useTopologies(projectId)
- const { mutate: deleteTopology } = useDeleteTopology({
- onError: (error) => setError(error),
- })
-
- const actions = ({ number }) => [
- {
- title: 'Delete Topology',
- onClick: () => deleteTopology({ projectId, number }),
- isDisabled: number === 0,
- },
- ]
-
- return (
- <>
- <AlertGroup isToast>
- {error && (
- <Alert
- isLiveRegion
- variant={AlertVariant.danger}
- title={error}
- actionClose={
- <AlertActionCloseButton
- title={error}
- variantLabel="danger alert"
- onClose={() => setError(null)}
- />
- }
- />
- )}
- </AlertGroup>
- <TableComposable aria-label="Topology List" variant="compact">
- <Thead>
- <Tr>
- <Th>Name</Th>
- <Th>Rooms</Th>
- <Th>Last Edited</Th>
- </Tr>
- </Thead>
- <Tbody>
- {topologies.map((topology) => (
- <Tr key={topology.id}>
- <Td dataLabel="Name">
- <Link href={`/projects/${projectId}/topologies/${topology.number}`}>
- {topology.name}
- </Link>
- </Td>
- <Td dataLabel="Rooms">
- {topology.rooms.length === 1 ? '1 room' : `${topology.rooms.length} rooms`}
- </Td>
- <Td dataLabel="Last Edited">{parseAndFormatDateTime(topology.updatedAt)}</Td>
- <Td isActionCell>
- <ActionsColumn items={actions(topology)} />
- </Td>
- </Tr>
- ))}
- {topologies.length === 0 && (
- <Tr>
- <Td colSpan={3}>
- <Bullseye>
- <TableEmptyState
- status={status}
- loadingTitle="Loading topologies"
- emptyTitle="No topologies"
- emptyText="You have not created any topology for this project yet. Click the New Topology button to create one."
- />
- </Bullseye>
- </Td>
- </Tr>
- )}
- </Tbody>
- </TableComposable>
- </>
- )
-}
-
-TopologyTable.propTypes = {
- projectId: PropTypes.number,
-}
-
-export default TopologyTable