@evenicanpm/admin-integrate 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +29 -0
  2. package/documents/ProcessExecution/fragments.ts +16 -0
  3. package/documents/ProcessExecution/list.ts +31 -0
  4. package/package.json +19 -0
  5. package/src/api/dashboard/queries/get-process-executions.query.ts +48 -0
  6. package/src/api/dashboard/queries/get-process-executions.server.ts +11 -0
  7. package/src/api/dashboard/queries/index.ts +1 -0
  8. package/src/auth/.keep +0 -0
  9. package/src/auth/types/.keep +0 -0
  10. package/src/components/breadcrumbs/breadcrumbs.tsx +42 -0
  11. package/src/components/breadcrumbs/index.ts +2 -0
  12. package/src/components/button/index.ts +1 -0
  13. package/src/components/button/outlined-icon-button.tsx +47 -0
  14. package/src/components/core/index.ts +1 -0
  15. package/src/components/core/loading.tsx +15 -0
  16. package/src/components/dashboard-list/dashboard-list-row.tsx +145 -0
  17. package/src/components/dashboard-list/dashboard-list.tsx +157 -0
  18. package/src/components/dashboard-list/index.ts +1 -0
  19. package/src/components/data-table/index.ts +3 -0
  20. package/src/components/data-table/table-header.tsx +109 -0
  21. package/src/components/data-table/table-pagination.tsx +36 -0
  22. package/src/components/data-table/table-row.tsx +27 -0
  23. package/src/components/data-table/table-skeleton.tsx +45 -0
  24. package/src/components/execution/execution-filter.ts +30 -0
  25. package/src/components/execution/execution-status-icon.tsx +30 -0
  26. package/src/components/execution/execution-tag.tsx +100 -0
  27. package/src/components/execution/index.ts +2 -0
  28. package/src/components/footer/footer.tsx +55 -0
  29. package/src/components/footer/index.ts +1 -0
  30. package/src/components/header/dashboard-list-header.tsx +88 -0
  31. package/src/components/header/execution-details-header.tsx +154 -0
  32. package/src/components/header/header.tsx +42 -0
  33. package/src/components/header/index.ts +3 -0
  34. package/src/components/icons/up-down.tsx +21 -0
  35. package/src/components/integration-view/edges/default-edge.tsx +23 -0
  36. package/src/components/integration-view/elk-layout-options.ts +39 -0
  37. package/src/components/integration-view/elk-types.ts +19 -0
  38. package/src/components/integration-view/flow-types.tsx +16 -0
  39. package/src/components/integration-view/index.ts +1 -0
  40. package/src/components/integration-view/integration-view.tsx +159 -0
  41. package/src/components/integration-view/nodes/connection-node.tsx +10 -0
  42. package/src/components/integration-view/nodes/entry-node.tsx +10 -0
  43. package/src/components/integration-view/nodes/group-node.tsx +10 -0
  44. package/src/components/integration-view/nodes/task-node.tsx +17 -0
  45. package/src/components/integration-view/temp-data/initialElements.ts +238 -0
  46. package/src/components/integration-view/types.ts +12 -0
  47. package/src/components/layouts/index.ts +2 -0
  48. package/src/components/layouts/main-layout.tsx +28 -0
  49. package/src/components/layouts/root-container.tsx +43 -0
  50. package/src/components/link-cards/index.ts +1 -0
  51. package/src/components/link-cards/link-card.tsx +36 -0
  52. package/src/components/link-cards/styles.ts +29 -0
  53. package/src/components/list-filter/date-filter.tsx +55 -0
  54. package/src/components/list-filter/date-range-filter.tsx +101 -0
  55. package/src/components/list-filter/index.ts +6 -0
  56. package/src/components/list-filter/list-filter-types.ts +70 -0
  57. package/src/components/list-filter/list-filter.tsx +409 -0
  58. package/src/components/list-filter/multi-select-filter.tsx +117 -0
  59. package/src/components/list-filter/select-filter.tsx +82 -0
  60. package/src/hooks/use-breadcrumbs.ts +45 -0
  61. package/src/hooks/use-timestamp.tsx +25 -0
  62. package/src/lib/.keep +0 -0
  63. package/src/pages/dashboard/dashboard-link-cards.tsx +122 -0
  64. package/src/pages/dashboard/dashboard-list-sections.tsx +149 -0
  65. package/src/pages/dashboard/dashboard.tsx +14 -0
  66. package/src/pages/dashboard/index.ts +1 -0
  67. package/src/pages/execution-details/execution-details.tsx +43 -0
  68. package/src/pages/execution-details/index.ts +1 -0
  69. package/src/pages/executions/executions-list/executions-list-row.tsx +142 -0
  70. package/src/pages/executions/executions-list/executions-list-table.tsx +78 -0
  71. package/src/pages/executions/executions-list/executions-list.tsx +265 -0
  72. package/src/pages/executions/executions-list/index.ts +1 -0
  73. package/src/pages/executions/executions.tsx +98 -0
  74. package/src/pages/executions/index.ts +1 -0
  75. package/src/pages/integrations/index.ts +1 -0
  76. package/src/pages/integrations/integrations.tsx +39 -0
  77. package/src/pages/scheduler/index.ts +1 -0
  78. package/src/pages/scheduler/scheduler.tsx +39 -0
  79. package/tsconfig.json +28 -0
@@ -0,0 +1,109 @@
1
+ import TableRow from "@mui/material/TableRow";
2
+ import TableHead from "@mui/material/TableHead";
3
+ import TableSortLabel from "@mui/material/TableSortLabel";
4
+ import TableCell, { TableCellProps } from "@mui/material/TableCell";
5
+ import styled from "@mui/material/styles/styled";
6
+
7
+ import { SxProps } from "@mui/material";
8
+ import { Theme, useTheme } from "@mui/material";
9
+ import { ExpandLess, ExpandMore } from "@mui/icons-material";
10
+
11
+ // STYLED COMPONENTS
12
+ const StyledTableCell = styled(TableCell)(({ theme }) => ({
13
+ fontWeight: 500,
14
+ padding: "16px 20px",
15
+ color: theme.palette.grey[900],
16
+ }));
17
+
18
+ // ==============================================================
19
+ export type Head = {
20
+ id: string;
21
+ label: string;
22
+ align: string;
23
+ width: string;
24
+ sortable?: boolean;
25
+ };
26
+ interface Props {
27
+ heading: Head[];
28
+ orderBy: string;
29
+ order: "asc" | "desc";
30
+ onRequestSort?: (id: string) => void; // if provided, make list sortable by column
31
+ rowSx?: SxProps;
32
+ }
33
+ // ==============================================================
34
+
35
+ export default function TableHeader({
36
+ order,
37
+ heading,
38
+ orderBy,
39
+ onRequestSort,
40
+ rowSx,
41
+ }: Props) {
42
+ const theme = useTheme();
43
+ const styles = getStyles(theme);
44
+
45
+ return (
46
+ <TableHead sx={{ backgroundColor: "grey.200" }}>
47
+ <TableRow sx={rowSx}>
48
+ {heading.map((headCell) => (
49
+ <StyledTableCell
50
+ key={headCell.id}
51
+ align={headCell.align as TableCellProps["align"]}
52
+ sortDirection={orderBy === headCell.id ? order : false}
53
+ width={headCell.width}
54
+ >
55
+ {onRequestSort && headCell.sortable ? (
56
+ <TableSortLabel
57
+ active={orderBy === headCell.id}
58
+ onClick={() => onRequestSort(headCell.id)}
59
+ direction={orderBy === headCell.id ? order : "asc"}
60
+ sx={styles.sortLabel}
61
+ IconComponent={() =>
62
+ order === "desc" && orderBy === headCell.id ? (
63
+ <ExpandMore
64
+ sx={{
65
+ ...styles.sortIcon,
66
+ visibility:
67
+ orderBy === headCell.id ? "visible" : "hidden",
68
+ }}
69
+ />
70
+ ) : (
71
+ <ExpandLess
72
+ sx={{
73
+ ...styles.sortIcon,
74
+ visibility:
75
+ orderBy === headCell.id ? "visible" : "hidden",
76
+ }}
77
+ />
78
+ )
79
+ }
80
+ >
81
+ {headCell.label}
82
+ </TableSortLabel>
83
+ ) : (
84
+ headCell.label
85
+ )}
86
+ </StyledTableCell>
87
+ ))}
88
+ </TableRow>
89
+ </TableHead>
90
+ );
91
+ }
92
+
93
+ const getStyles = (theme: Theme) => {
94
+ return {
95
+ sortLabel: {
96
+ "&:hover": {
97
+ color: "info.main",
98
+ "& .MuiSvgIcon-root": {
99
+ color: "info.main",
100
+ visibility: "visible",
101
+ },
102
+ },
103
+ },
104
+ sortIcon: {
105
+ marginLeft: theme.spacing(1),
106
+ color: "grey.800",
107
+ },
108
+ };
109
+ };
@@ -0,0 +1,36 @@
1
+ import Pagination, { PaginationProps } from "@mui/material/Pagination";
2
+ import styled from "@mui/material/styles/styled";
3
+
4
+ // STYLED COMPONENT
5
+ const StyledPagination = styled(Pagination)(({ theme }) => ({
6
+ "& .MuiPaginationItem-root": {
7
+ fontSize: 14,
8
+ fontWeight: 500,
9
+ color: theme.palette.grey[900],
10
+ border: `1px solid transparent`,
11
+ },
12
+ "& .MuiPaginationItem-page:hover": {
13
+ borderRadius: 20,
14
+ backgroundColor: "transparent",
15
+ color: theme.palette.info.main,
16
+ border: `1px solid ${theme.palette.info.main}`,
17
+ },
18
+ "& .MuiPaginationItem-page.Mui-selected": {
19
+ borderRadius: 20,
20
+ backgroundColor: "transparent",
21
+ color: theme.palette.info.main,
22
+ border: `1px solid ${theme.palette.info.main}`,
23
+ ":hover": { backgroundColor: "transparent" },
24
+ },
25
+ "& .MuiPaginationItem-previousNext": {
26
+ margin: 10,
27
+ borderRadius: 20,
28
+ color: theme.palette.info.main,
29
+ border: `1px solid ${theme.palette.info.main}`,
30
+ "&:hover": { backgroundColor: "transparent" },
31
+ },
32
+ }));
33
+
34
+ export default function TablePagination(props: PaginationProps) {
35
+ return <StyledPagination {...props} />;
36
+ }
@@ -0,0 +1,27 @@
1
+ import styled from "@mui/material/styles/styled";
2
+ import { TableRow, TableCell, IconButton } from "@mui/material";
3
+
4
+ const StyledTableCell = styled(TableCell)(({ theme }) => ({
5
+ fontSize: 14,
6
+ paddingTop: 10,
7
+ fontWeight: 500,
8
+ paddingBottom: 10,
9
+ color: theme.palette.grey[900],
10
+ borderBottom: `1px solid ${theme.palette.grey[300]}`,
11
+ }));
12
+
13
+ const StyledTableRow = styled(TableRow)({
14
+ ":last-child .MuiTableCell-root": { border: 0 },
15
+ "&.Mui-selected": {
16
+ backgroundColor: "transparent",
17
+ ":hover": { backgroundColor: "transparent" },
18
+ },
19
+ });
20
+
21
+ const StyledIconButton = styled(IconButton)(({ theme }) => ({
22
+ color: theme.palette.grey[600],
23
+ "& .MuiSvgIcon-root": { fontSize: 19 },
24
+ ":hover": { color: theme.palette.info.main },
25
+ }));
26
+
27
+ export { StyledTableCell, StyledTableRow, StyledIconButton };
@@ -0,0 +1,45 @@
1
+ import { TableContainer, Table, TableBody, Skeleton } from "@mui/material";
2
+ import { SxProps } from "@mui/material";
3
+
4
+ import TableHeader, { Head } from "./table-header";
5
+ import { StyledTableRow, StyledTableCell } from "./table-row";
6
+
7
+ interface Props {
8
+ heading: Head[];
9
+ perPage: number;
10
+ sx?: SxProps;
11
+ rowSx?: SxProps;
12
+ }
13
+
14
+ export default function TableSkeleton(props: Props) {
15
+ const rangeArray: number[] = Array.from(
16
+ { length: props.perPage },
17
+ (_, i) => i,
18
+ );
19
+ return (
20
+ <TableContainer sx={props.sx}>
21
+ <Table>
22
+ <TableHeader
23
+ heading={props.heading}
24
+ order="asc"
25
+ orderBy=""
26
+ onRequestSort={() => {
27
+ // do nothing
28
+ }}
29
+ rowSx={props.rowSx}
30
+ />
31
+ <TableBody>
32
+ {rangeArray.map((i: number) => (
33
+ <StyledTableRow key={i} sx={props.rowSx}>
34
+ {props.heading.map((heading: Head) => (
35
+ <StyledTableCell key={`${i}_${heading.id}`}>
36
+ <Skeleton />
37
+ </StyledTableCell>
38
+ ))}
39
+ </StyledTableRow>
40
+ ))}
41
+ </TableBody>
42
+ </Table>
43
+ </TableContainer>
44
+ );
45
+ }
@@ -0,0 +1,30 @@
1
+ import {
2
+ DateRangeFilter,
3
+ Filters,
4
+ MultiSelectFilter,
5
+ } from "@evenicanpm/admin-integrate/components/list-filter/list-filter-types";
6
+
7
+ export interface ExecutionsListFilters extends Filters {
8
+ dateRange: DateRangeFilter;
9
+ multiSelectFilter: MultiSelectFilter;
10
+ }
11
+
12
+ export const executionListFilters: ExecutionsListFilters = {
13
+ dateRange: {
14
+ type: "dateRange",
15
+ label: "Date Range",
16
+ value: null,
17
+ },
18
+ multiSelectFilter: {
19
+ type: "multiSelect",
20
+ label: "Execution Status",
21
+ value: null,
22
+ options: [
23
+ { id: 300, value: "Executions.inProgress" },
24
+ { id: 400, value: "Executions.complete" },
25
+ { id: 450, value: "Executions.warning" },
26
+ { id: 500, value: "Executions.failed" },
27
+ { id: 999, value: "Executions.unavailable" },
28
+ ],
29
+ },
30
+ };
@@ -0,0 +1,30 @@
1
+ import React from "react";
2
+
3
+ import { Box, Typography, SxProps } from "@mui/material";
4
+ import {
5
+ RemoveRedEye,
6
+ Loop,
7
+ CheckCircleOutline,
8
+ WarningAmber,
9
+ ErrorOutline,
10
+ Clear,
11
+ } from "@mui/icons-material";
12
+
13
+ interface Props {
14
+ executionStatusId: string;
15
+ sx?: SxProps;
16
+ }
17
+
18
+ const ExecutionStatusIcon: React.FC<Props> = (props: Props) => {
19
+ const statusIconMap = {
20
+ 300: <Loop color="info" />,
21
+ 400: <CheckCircleOutline color="success" />,
22
+ 450: <WarningAmber color="warning" />,
23
+ 500: <ErrorOutline color="error" />,
24
+ 999: <Clear sx={{ color: "grey.600" }} />,
25
+ };
26
+
27
+ return statusIconMap[props.executionStatusId] || statusIconMap[999];
28
+ };
29
+
30
+ export default ExecutionStatusIcon;
@@ -0,0 +1,100 @@
1
+ import React from "react";
2
+ import { useTranslations } from "next-intl";
3
+
4
+ import { styled } from "@mui/material/styles";
5
+ import { Box, Typography, SxProps } from "@mui/material";
6
+
7
+ // Styled components
8
+ const StyledTagBox = styled(Box)(({ theme }) => ({
9
+ padding: theme.spacing(0, 1.5),
10
+ border: "solid 1px",
11
+ borderRadius: 4,
12
+ alignContent: "center",
13
+ maxWidth: "fit-content",
14
+ }));
15
+ const StyledTag = styled(Typography)(({ theme }) => ({
16
+ fontWeight: "bold",
17
+ }));
18
+
19
+ interface Props {
20
+ executionStatusId: string;
21
+ sx?: SxProps;
22
+ }
23
+
24
+ const ExecutionTag: React.FC<Props> = (props: Props) => {
25
+ const t = useTranslations("Integrate");
26
+
27
+ const statusTagMap = {
28
+ 300: (
29
+ <StyledTagBox
30
+ sx={{
31
+ backgroundColor: "info.100",
32
+ borderColor: "info.main",
33
+ ...props.sx,
34
+ }}
35
+ >
36
+ <StyledTag color="info" variant="caption">
37
+ {t("Executions.inProgress")}
38
+ </StyledTag>
39
+ </StyledTagBox>
40
+ ),
41
+ 400: (
42
+ <StyledTagBox
43
+ sx={{
44
+ backgroundColor: "success.100",
45
+ borderColor: "success.main",
46
+ ...props.sx,
47
+ }}
48
+ >
49
+ <StyledTag color="success" variant="caption">
50
+ {t("Executions.complete")}
51
+ </StyledTag>
52
+ </StyledTagBox>
53
+ ),
54
+ 450: (
55
+ <StyledTagBox
56
+ sx={{
57
+ backgroundColor: "warning.100",
58
+ borderColor: "orange.700",
59
+ color: "orange.700",
60
+ ...props.sx,
61
+ }}
62
+ >
63
+ <StyledTag color="inherit" variant="caption">
64
+ {t("Executions.warning")}
65
+ </StyledTag>
66
+ </StyledTagBox>
67
+ ),
68
+ 500: (
69
+ <StyledTagBox
70
+ sx={{
71
+ backgroundColor: "error.100",
72
+ borderColor: "error.main",
73
+ ...props.sx,
74
+ }}
75
+ >
76
+ <StyledTag color="error" variant="caption">
77
+ {t("Executions.failed")}
78
+ </StyledTag>
79
+ </StyledTagBox>
80
+ ),
81
+ 999: (
82
+ <StyledTagBox
83
+ sx={{
84
+ backgroundColor: "grey.300",
85
+ borderColor: "grey.600",
86
+ color: "grey.600",
87
+ ...props.sx,
88
+ }}
89
+ >
90
+ <StyledTag color="inherit" variant="caption">
91
+ {t("Executions.unavailable")}
92
+ </StyledTag>
93
+ </StyledTagBox>
94
+ ),
95
+ };
96
+
97
+ return statusTagMap[props.executionStatusId] || statusTagMap[999];
98
+ };
99
+
100
+ export default ExecutionTag;
@@ -0,0 +1,2 @@
1
+ export { default as ExecutionTag } from "./execution-tag";
2
+ export { default as ExecutionStatusIcon } from "./execution-status-icon";
@@ -0,0 +1,55 @@
1
+ import React from "react";
2
+ import { useTranslations } from "next-intl";
3
+
4
+ // MUI
5
+ import { Theme, useTheme } from "@mui/material";
6
+ import styled from "@mui/material/styles/styled";
7
+ import { Typography, Box, Grid2 as Grid } from "@mui/material";
8
+ import { Copyright } from "@mui/icons-material";
9
+
10
+ // Styled components
11
+ const Footer = styled("footer")(({ theme }) => ({
12
+ flexShrink: 0,
13
+ marginTop: "auto",
14
+ }));
15
+
16
+ const IntegrateFooter: React.FC = () => {
17
+ const t = useTranslations("Integrate.Footer");
18
+
19
+ const theme = useTheme();
20
+ const styles = getStyles(theme);
21
+
22
+ return (
23
+ <Footer>
24
+ <Grid container spacing={0.5} sx={styles.container}>
25
+ <Grid>
26
+ <Box sx={{ ...styles.centered, color: "grey.500" }}>
27
+ <Copyright color="inherit" fontSize="small" />
28
+ </Box>
29
+ </Grid>
30
+ <Grid>
31
+ <Box sx={styles.centered}>
32
+ <Typography variant="caption" color="grey">
33
+ {t("copyright")}
34
+ </Typography>
35
+ </Box>
36
+ </Grid>
37
+ </Grid>
38
+ </Footer>
39
+ );
40
+ };
41
+
42
+ const getStyles = (theme: Theme) => {
43
+ return {
44
+ container: {
45
+ justifyContent: "center",
46
+ padding: theme.spacing(5, 0, 0),
47
+ },
48
+ centered: {
49
+ display: "grid",
50
+ alignItems: "center",
51
+ },
52
+ };
53
+ };
54
+
55
+ export default IntegrateFooter;
@@ -0,0 +1 @@
1
+ export { default as IntegrateFooter } from "./footer";
@@ -0,0 +1,88 @@
1
+ import React from "react";
2
+ import { redirect, RedirectType } from "next/navigation";
3
+ import { useTranslations } from "next-intl";
4
+
5
+ // MUI
6
+ import { Theme, useTheme } from "@mui/material";
7
+ import { Grid2 as Grid, Box, Typography, Button } from "@mui/material";
8
+ import { Refresh, VisibilityOutlined } from "@mui/icons-material";
9
+
10
+ // Custom components
11
+ import { OutlinedIconButton } from "../button";
12
+
13
+ interface Props {
14
+ title: string;
15
+ url?: string;
16
+ handleRefresh?: () => void;
17
+ }
18
+
19
+ const DashboardListHeader: React.FC<Props> = (props: Props) => {
20
+ const t = useTranslations("Integrate.Dashboard");
21
+
22
+ const theme = useTheme();
23
+ const styles = getStyles(theme);
24
+
25
+ const handleViewAll = () => {
26
+ //Temporarily commented out until executions detail page is implemented
27
+ //redirect(props.url, RedirectType.push);
28
+ };
29
+
30
+ return (
31
+ <Grid container spacing={0}>
32
+ <Grid size={8}>
33
+ <Box sx={styles.title}>
34
+ <Typography variant="h6" sx={styles.typography}>
35
+ {props.title}
36
+ </Typography>
37
+ </Box>
38
+ </Grid>
39
+ <Grid size={4}>
40
+ <Box display="flex" justifyContent="flex-end">
41
+ {props.url && (
42
+ <Button
43
+ sx={styles.button}
44
+ variant="outlined"
45
+ color="info"
46
+ startIcon={<VisibilityOutlined />}
47
+ onClick={handleViewAll}
48
+ >
49
+ {t("ListHeader.viewAll")}
50
+ </Button>
51
+ )}
52
+ {props.handleRefresh && (
53
+ <OutlinedIconButton
54
+ sx={styles.iconButton}
55
+ color="info"
56
+ onClick={props.handleRefresh}
57
+ >
58
+ <Refresh />
59
+ </OutlinedIconButton>
60
+ )}
61
+ </Box>
62
+ </Grid>
63
+ </Grid>
64
+ );
65
+ };
66
+
67
+ const getStyles = (theme: Theme) => {
68
+ return {
69
+ title: {
70
+ display: "flex",
71
+ alignItems: "center",
72
+ height: "100%",
73
+ },
74
+ typography: {
75
+ fontWeight: "medium",
76
+ },
77
+ button: {
78
+ borderWidth: 2,
79
+ borderRadius: 1,
80
+ marginRight: theme.spacing(1),
81
+ },
82
+ iconButton: {
83
+ borderRadius: 1,
84
+ },
85
+ };
86
+ };
87
+
88
+ export default DashboardListHeader;
@@ -0,0 +1,154 @@
1
+ import React, { useState } from "react";
2
+
3
+ // MUI
4
+ import { Theme, useTheme } from "@mui/material";
5
+ import {
6
+ Grid2 as Grid,
7
+ Box,
8
+ Typography,
9
+ Button,
10
+ Stack,
11
+ Popover,
12
+ } from "@mui/material";
13
+ import { PlayArrowOutlined } from "@mui/icons-material";
14
+
15
+ // Custom components
16
+ import ExecutionTag from "../execution/execution-tag";
17
+
18
+ interface Execution {
19
+ name: string;
20
+ startDate: string;
21
+ endDate: string;
22
+ executionStatus: {
23
+ id: string;
24
+ };
25
+ }
26
+
27
+ interface Props {
28
+ execution: Execution;
29
+ handleRun?: () => void;
30
+ }
31
+
32
+ const ExecutionDetailsHeader: React.FC<Props> = (props: Props) => {
33
+ const theme = useTheme();
34
+ const styles = getStyles(theme);
35
+
36
+ const startDate = new Date(props.execution.startDate);
37
+ const endDate = new Date(props.execution.endDate);
38
+ const localTime = startDate.toLocaleString();
39
+ const duration = (endDate.getTime() - startDate.getTime()) / 1000;
40
+
41
+ const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
42
+ const open = Boolean(anchorEl);
43
+
44
+ const handlePopoverOpen = (event: React.MouseEvent<HTMLElement>) => {
45
+ setAnchorEl(event.currentTarget);
46
+ };
47
+
48
+ const handlePopoverClose = () => {
49
+ setAnchorEl(null);
50
+ };
51
+
52
+ return (
53
+ <Grid container spacing={0}>
54
+ <Grid size={8}>
55
+ <Stack spacing={5} direction="row">
56
+ <Typography variant="h5" sx={styles.typography}>
57
+ {props.execution.name}
58
+ </Typography>
59
+ <ExecutionTag
60
+ executionStatusId={props.execution.executionStatus.id}
61
+ />
62
+ </Stack>
63
+ </Grid>
64
+ <Grid size={4}>
65
+ <Box display="flex" justifyContent="flex-end">
66
+ {props.handleRun && (
67
+ <Button
68
+ sx={styles.button}
69
+ variant="contained"
70
+ color="info"
71
+ startIcon={<PlayArrowOutlined />}
72
+ onClick={props.handleRun}
73
+ >
74
+ Run
75
+ </Button>
76
+ )}
77
+ </Box>
78
+ </Grid>
79
+ <Grid size={12}>
80
+ <Stack
81
+ spacing={1}
82
+ direction="row"
83
+ onMouseEnter={handlePopoverOpen}
84
+ onMouseLeave={handlePopoverClose}
85
+ >
86
+ <Typography color="grey" variant="caption">
87
+ {localTime}
88
+ </Typography>
89
+ <Typography color="grey" variant="caption">
90
+ Duration: {duration}s
91
+ </Typography>
92
+ </Stack>
93
+ </Grid>
94
+ <Popover
95
+ sx={{ pointerEvents: "none" }}
96
+ open={open}
97
+ anchorEl={anchorEl}
98
+ anchorOrigin={{
99
+ vertical: "bottom",
100
+ horizontal: "left",
101
+ }}
102
+ transformOrigin={{
103
+ vertical: "top",
104
+ horizontal: "left",
105
+ }}
106
+ onClose={handlePopoverClose}
107
+ disableRestoreFocus
108
+ >
109
+ <Box sx={styles.popover}>
110
+ <Typography variant="body2">
111
+ Uses your browser's local timezone.
112
+ </Typography>
113
+ <Box
114
+ display={
115
+ Intl.DateTimeFormat().resolvedOptions().timeZone
116
+ ? "block"
117
+ : "none"
118
+ }
119
+ >
120
+ <Typography variant="body2" align="center" sx={styles.timezone}>
121
+ {Intl.DateTimeFormat().resolvedOptions().timeZone}
122
+ </Typography>
123
+ </Box>
124
+ </Box>
125
+ </Popover>
126
+ </Grid>
127
+ );
128
+ };
129
+
130
+ const getStyles = (theme: Theme) => {
131
+ return {
132
+ title: {
133
+ display: "flex",
134
+ alignItems: "center",
135
+ height: "100%",
136
+ },
137
+ typography: {
138
+ fontWeight: "medium",
139
+ },
140
+ button: {
141
+ borderWidth: 2,
142
+ borderRadius: 1,
143
+ marginRight: theme.spacing(1),
144
+ },
145
+ popover: {
146
+ padding: theme.spacing(1),
147
+ },
148
+ timezone: {
149
+ color: "info.main",
150
+ },
151
+ };
152
+ };
153
+
154
+ export default ExecutionDetailsHeader;