@roadiehq/backstage-plugin-jira
Version:
223 lines (220 loc) • 10 kB
JavaScript
import React, { useState, useEffect } from 'react';
import { makeStyles, createStyles, Box, IconButton, Menu, MenuItem, Checkbox, Button, Grid, Typography, Divider, TableContainer, Table, TableHead, TableRow, TableCell, TableBody, TablePagination, Avatar } from '@material-ui/core';
import Alert from '@material-ui/lab/Alert';
import { InfoCard, Progress } from '@backstage/core-components';
import { useEntity } from '@backstage/plugin-catalog-react';
import 'xml-js';
import 'luxon';
import 'uuid';
import { useAnalytics } from '@backstage/core-plugin-api';
import 'react-use';
import '../../api/index.esm.js';
import { useProjectInfo } from '../../hooks/useProjectInfo.esm.js';
import { useProjectEntity } from '../../hooks/useProjectEntity.esm.js';
import { Status } from './components/Status.esm.js';
import { ActivityStream } from '../EntityJiraActivityStreamCard/ActivityStream.esm.js';
import { Selectors } from './components/Selectors.esm.js';
import { useEmptyIssueTypeFilter } from '../../hooks/useEmptyIssueTypeFilter.esm.js';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import ArrowForwardIcon from '@material-ui/icons/ArrowForward';
const useStyles = makeStyles(
(theme) => createStyles({
infoCard: {
marginBottom: theme.spacing(3),
"& + .MuiAlert-root": {
marginTop: theme.spacing(3)
}
},
root: {
flexGrow: 1,
fontSize: "0.75rem",
"& > * + *": {
marginTop: theme.spacing(1)
}
},
ticketLink: {
color: theme.palette.link,
textDecoration: "none",
transition: "color 0.3s",
"&:hover": {
color: theme.palette.linkHover
}
}
})
);
const CardProjectDetails = ({
project,
component
}) => /* @__PURE__ */ React.createElement(Box, { display: "inline-flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(Avatar, { alt: "", src: project.iconUrl }), /* @__PURE__ */ React.createElement(Box, { ml: 1 }, project.name, " | ", project.type, component ? /* @__PURE__ */ React.createElement(Box, null, "component: ", component) : null));
const JiraOverviewCard = (props) => {
const { hideIssueFilter } = props;
const { entity } = useEntity();
const classes = useStyles();
const analytics = useAnalytics();
const { projectKey, component, tokenType, label } = useProjectEntity(entity);
const [statusesNames, setStatusesNames] = useState([]);
const {
project,
issues,
tickets,
ticketIds,
projectLoading,
projectError,
fetchProjectInfo
} = useProjectInfo(projectKey, component, label, statusesNames);
const {
issueTypes: displayIssues,
type,
changeType
} = useEmptyIssueTypeFilter(issues);
const [anchorEl, setAnchorEl] = React.useState(null);
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
useEffect(() => {
setPage(0);
}, [tickets]);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return /* @__PURE__ */ React.createElement(
InfoCard,
{
className: classes.infoCard,
title: "Jira",
subheader: project && /* @__PURE__ */ React.createElement(
Box,
{
display: "flex",
justifyContent: "space-between",
width: "100%",
alignItems: "center"
},
/* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(CardProjectDetails, { project, component }), /* @__PURE__ */ React.createElement(Box, { display: "inline-flex", pl: 1 }, /* @__PURE__ */ React.createElement(
IconButton,
{
"aria-label": "more",
"aria-controls": "long-menu",
"aria-haspopup": "true",
onClick: handleClick
},
/* @__PURE__ */ React.createElement(MoreVertIcon, null)
), /* @__PURE__ */ React.createElement(
Menu,
{
id: "simple-menu",
anchorEl,
keepMounted: true,
open: Boolean(anchorEl),
onClose: handleClose
},
/* @__PURE__ */ React.createElement(MenuItem, { onClick: changeType }, /* @__PURE__ */ React.createElement(Checkbox, { checked: type === "all" }), /* @__PURE__ */ React.createElement(React.Fragment, null, "Show empty issue types"))
))),
/* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(
Button,
{
variant: "outlined",
color: "primary",
size: "medium",
endIcon: /* @__PURE__ */ React.createElement(ArrowForwardIcon, null),
href: `${project?.url}/browse/${projectKey}`,
target: "_blank"
},
"Open in JIRA"
))
)
},
projectLoading && !(project && issues) ? /* @__PURE__ */ React.createElement(Progress, null) : null,
projectError ? /* @__PURE__ */ React.createElement(Alert, { severity: "error", className: classes.infoCard }, projectError.message) : null,
project && issues ? /* @__PURE__ */ React.createElement("div", { className: classes.root }, !hideIssueFilter && /* @__PURE__ */ React.createElement(
Selectors,
{
projectKey,
statusesNames,
setStatusesNames,
fetchProjectInfo
}
), /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 3 }, displayIssues?.map((issueType) => /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true, key: issueType.name }, /* @__PURE__ */ React.createElement(
Box,
{
width: 100,
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center"
},
/* @__PURE__ */ React.createElement(Status, { name: issueType.name, iconUrl: issueType.iconUrl }),
/* @__PURE__ */ React.createElement(Typography, { variant: "h4" }, issueType.total)
)))), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(TableContainer, null, /* @__PURE__ */ React.createElement(Table, { className: classes.infoCard, "aria-label": "tickets-table" }, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, "Key"), /* @__PURE__ */ React.createElement(TableCell, { align: "left" }, "Summary"), /* @__PURE__ */ React.createElement(TableCell, { align: "left" }, "Priority"), /* @__PURE__ */ React.createElement(TableCell, { align: "left" }, "Status"), /* @__PURE__ */ React.createElement(TableCell, { align: "left" }, "Created"), /* @__PURE__ */ React.createElement(TableCell, { align: "left" }, "Updated"), /* @__PURE__ */ React.createElement(TableCell, { align: "left" }, "Assignee"))), /* @__PURE__ */ React.createElement(TableBody, null, tickets && tickets.length > 0 ? tickets.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((ticket) => /* @__PURE__ */ React.createElement(TableRow, { key: ticket?.key }, /* @__PURE__ */ React.createElement(TableCell, { component: "th" }, /* @__PURE__ */ React.createElement(
"a",
{
href: `${project?.url}/browse/${ticket?.key}`,
target: "_blank",
rel: "noopener noreferrer",
className: classes.ticketLink
},
ticket?.key
)), /* @__PURE__ */ React.createElement(TableCell, { align: "left" }, ticket?.summary), /* @__PURE__ */ React.createElement(TableCell, { align: "left" }, /* @__PURE__ */ React.createElement(
"img",
{
src: ticket?.priority?.iconUrl,
alt: ticket?.priority?.name,
title: ticket?.priority?.name,
width: "20px"
}
)), /* @__PURE__ */ React.createElement(TableCell, { align: "left" }, ticket?.status), /* @__PURE__ */ React.createElement(TableCell, { align: "left" }, new Date(ticket?.created).toLocaleDateString()), /* @__PURE__ */ React.createElement(TableCell, { align: "left" }, new Date(ticket?.updated).toLocaleDateString()), /* @__PURE__ */ React.createElement(
TableCell,
{
align: "left",
style: { display: "flex", alignItems: "center" }
},
ticket?.assignee?.displayName ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
"img",
{
src: ticket?.assignee?.avatarUrl,
alt: ticket?.assignee?.displayName,
title: ticket?.assignee?.displayName,
style: { marginRight: "10px" },
width: "30px"
}
), ticket?.assignee?.displayName) : /* @__PURE__ */ React.createElement("span", null, "Not Assigned")
))) : /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, { colSpan: 7, align: "center" }, "No Jira tickets available.")))), /* @__PURE__ */ React.createElement(
TablePagination,
{
rowsPerPageOptions: [5, 10, 20],
component: "div",
count: tickets?.length ?? 0,
rowsPerPage,
page,
onPageChange: (_event, newPage) => {
setPage(newPage);
analytics.captureEvent(
"paginate",
`page: ${newPage}, size: ${rowsPerPage}`
);
},
onRowsPerPageChange: (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
analytics.captureEvent(
"paginate",
`page: 0, size: ${parseInt(event.target.value, 10)}`
);
}
}
)), /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1" }, "Activity Stream"), /* @__PURE__ */ React.createElement(
ActivityStream,
{
projectKey,
tokenType,
componentName: component,
label,
ticketIds
}
))) : null
);
};
export { JiraOverviewCard };
//# sourceMappingURL=JiraOverviewCard.esm.js.map