diff --git a/src/App.tsx b/src/App.tsx index c5eb0110..de7ff626 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,6 +3,7 @@ import { Layout } from "./components/layout" import { Login } from "./components/Login" import { ProjectsView } from "./components/ProjectsView" import { BacklogView } from "./components/BacklogView" +import { EpicView } from "./components/EpicView" import { StoryMapView } from "./components/StoryMapView" import { StoryMapDashboard } from "./components/StoryMapView/StoryMapDashboard" @@ -13,6 +14,7 @@ export function App() { }> } /> } /> + } /> } /> } /> diff --git a/src/components/CreateIssue/CreateIssueModal.tsx b/src/components/CreateIssue/CreateIssueModal.tsx index 00770cca..2103d7cd 100644 --- a/src/components/CreateIssue/CreateIssueModal.tsx +++ b/src/components/CreateIssue/CreateIssueModal.tsx @@ -117,6 +117,7 @@ export function CreateIssueModal({ color: "green", }) queryClient.invalidateQueries({ queryKey: ["issues"] }) + queryClient.invalidateQueries({ queryKey: ["epics"] }) setOpened(false) form.reset() }, diff --git a/src/components/EpicView/EpicCard.tsx b/src/components/EpicView/EpicCard.tsx new file mode 100644 index 00000000..dc80d299 --- /dev/null +++ b/src/components/EpicView/EpicCard.tsx @@ -0,0 +1,194 @@ +import {Issue} from "types"; +import { + Avatar, + Badge, + Box, + Center, + Grid, + Group, + Modal, + Stack, + Text, ThemeIcon, + Tooltip, + useMantineTheme +} from "@mantine/core"; +import {useHover} from "@mantine/hooks"; +import {useState} from "react"; +import {useQueryClient} from "@tanstack/react-query"; +import {IconBolt} from "@tabler/icons"; +import {DeleteButton} from "../BacklogView/Issue/DeleteButton"; + +export function EpicCard ({ + issueKey, + summary, + status, + storyPointsEstimate, + epic, + labels, + assignee, +}: Issue) { + let storyPointsColor: string + const [opened, setOpened] = useState(false) + const queryClient = useQueryClient() + const {hovered} = useHover() + const theme = useMantineTheme() + const hoverStyles = + theme.colorScheme === "dark" + ? { + backgroundColor: theme.colors.dark[8], + transition: "background-color .1s ease-in", + } + : { + backgroundColor: theme.colors.gray[1], + transition: "background-color .1s ease-in", + } + switch (status) { + case "To Do": + storyPointsColor = "gray.6" + break + case "In Progress": + storyPointsColor = "blue.8" + break + case "Done": + storyPointsColor = "green.9" + break + default: + storyPointsColor = "gray.6" + } + return ( + <> + + + +
+ + + +
+
+ + + + + {issueKey} + + {epic && ( + + {epic} + + )} + {labels?.length !== 0 && + labels.map((label) => ( + + {label} + + ))} + + {summary} + + + + + {assignee?.avatarUrls !== undefined ? ( + + ) : ( + + )} + + + + + + {storyPointsEstimate} + + + +
+ { + setOpened(false) + queryClient.invalidateQueries({ queryKey: ["issues"] }) + }} + size="90vw" + overflow="outside" + overlayOpacity={0.55} + overlayBlur={3} + withCloseButton={false} + > + {/* TODO open Epic Detail View */} + + + ) +} diff --git a/src/components/EpicView/EpicView.tsx b/src/components/EpicView/EpicView.tsx new file mode 100644 index 00000000..b6651534 --- /dev/null +++ b/src/components/EpicView/EpicView.tsx @@ -0,0 +1,122 @@ +import {Group, Stack, Text, Title, ScrollArea, Box, Button, Center, Loader} from "@mantine/core"; +import {useNavigate} from "react-router-dom"; +import {useState} from "react"; +import {useQuery} from "@tanstack/react-query"; +import {useCanvasStore} from "../../lib/Store"; +import {CreateIssueModal} from "../CreateIssue/CreateIssueModal"; +import {Issue} from "../../../types"; +import {EpicWrapper} from "./EpicWrapper"; +import {getEpics} from "./helpers/queryFetchers"; + + +export function EpicView() { + const navigate = useNavigate() + const projectName = useCanvasStore((state) => state.selectedProject?.name) + const [createIssueModalOpened, setCreateIssueModalOpened] = useState(false) + const projectKey = useCanvasStore((state) => state.selectedProject?.key) + const [EpicWrappers, setEpicWrappers] = useState( + new Map() + ) + + const updateEpicWrapper = ( + key: string, + value: { issues: Issue[]} + ) => { + setEpicWrappers((map) => new Map(map.set(key, value))) + } + + const {isLoading: isLoadingEpics} = + useQuery({ + queryKey: ["epics", projectKey], + queryFn: () => getEpics(projectKey), + enabled: !!projectKey, + onSuccess: (epics) => { + updateEpicWrapper("EpicView", { + issues: + epics && epics instanceof Array ? epics : [] + }) + }, + }) + if (isLoadingEpics) + return ( +
+ {projectKey ? ( + + ) : ( + + No Project has been selected! + + Please go back to the Projects View section and select a project + + + + )} +
+ ) + return ( + + + + + navigate("/projectsview")} + sx={{ + ":hover": { + textDecoration: "underline", + cursor: "pointer", + }, + }} + > + Projects + + / + {projectName} + + + Epics + + + {EpicWrappers.get("EpicView") &&( + + + + )} + + + + + + + ) +} diff --git a/src/components/EpicView/EpicWrapper.tsx b/src/components/EpicView/EpicWrapper.tsx new file mode 100644 index 00000000..91bbda03 --- /dev/null +++ b/src/components/EpicView/EpicWrapper.tsx @@ -0,0 +1,18 @@ +import {Stack} from "@mantine/core"; +import {Issue} from "../../../types"; +import {EpicCard} from "./EpicCard"; + + +export function EpicWrapper({ + epics, +}: { + epics: Issue[] +}){ + return ( + + {epics.map((epic: Issue) => ( + + ))} + + ) +} diff --git a/src/components/EpicView/helpers/queryFetchers.ts b/src/components/EpicView/helpers/queryFetchers.ts new file mode 100644 index 00000000..c5605f3c --- /dev/null +++ b/src/components/EpicView/helpers/queryFetchers.ts @@ -0,0 +1,6 @@ +import {Issue} from "../../../../types"; + +export const getEpics = ( + projectKey: string | undefined +): Promise => + window.provider.getEpicsByProject(projectKey || "") diff --git a/src/components/EpicView/index.ts b/src/components/EpicView/index.ts new file mode 100644 index 00000000..01648816 --- /dev/null +++ b/src/components/EpicView/index.ts @@ -0,0 +1 @@ +export * from "./EpicView" diff --git a/src/components/layout/LayoutHeader.tsx b/src/components/layout/LayoutHeader.tsx index f03b7c5e..bea190d5 100644 --- a/src/components/layout/LayoutHeader.tsx +++ b/src/components/layout/LayoutHeader.tsx @@ -50,6 +50,12 @@ export function LayoutHeader() { > Backlog + navigate("/epicview")}> + Epics +