From c7967f70927b3d462c7a6d46d705783e08220a53 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Maximilian=20R=C3=BCsch?=
<78490564+maximilianruesch@users.noreply.github.com>
Date: Mon, 11 Mar 2024 18:58:57 +0100
Subject: [PATCH] Simplify and fix issue creation (#145)
---
src/components/BacklogView/BacklogView.tsx | 6 +-
.../CreateIssue/CreateIssueModal.tsx | 210 +-----------------
.../CreateIssue/CreateIssueModalContent.tsx | 179 +++++++++++++++
.../CreateIssue/Fields/EpicSelect.tsx | 3 +-
.../CreateIssue/Fields/LabelsSelect.tsx | 3 +-
.../CreateIssue/Fields/PrioritySelect.tsx | 3 +-
.../CreateIssue/Fields/SprintSelect.tsx | 5 +-
src/components/CreateIssue/queryFunctions.ts | 40 ----
.../DetailView/Components/IssueSprint.tsx | 5 +-
.../DetailView/Components/IssueStatusMenu.tsx | 5 +-
.../DetailView/Components/Labels.tsx | 3 +-
.../DetailView/Components/ReporterMenu.tsx | 4 +-
.../SplitIssue/SplitIssueCreate.tsx | 18 +-
.../Components/StoryPointsEstimateMenu.tsx | 3 +-
.../EpicDetailView/Components/ChildIssues.tsx | 3 +-
src/components/EpicView/EpicView.tsx | 3 +-
src/components/layout/LayoutHeader.tsx | 3 +-
17 files changed, 216 insertions(+), 280 deletions(-)
create mode 100644 src/components/CreateIssue/CreateIssueModalContent.tsx
delete mode 100644 src/components/CreateIssue/queryFunctions.ts
diff --git a/src/components/BacklogView/BacklogView.tsx b/src/components/BacklogView/BacklogView.tsx
index 01f935da..8ac92758 100644
--- a/src/components/BacklogView/BacklogView.tsx
+++ b/src/components/BacklogView/BacklogView.tsx
@@ -146,7 +146,11 @@ export function BacklogView() {
isLoadingContent={isFetchingSprints || issueQueries.some((query) => query.isFetching)}
>
-
+ setCreateIssueModalOpened(false)}
+ onCreate={() => setCreateIssueModalOpened(false)}
+ />
>,
+ onCancel: () => void,
+ onCreate: () => void,
}) {
- const queryClient = useQueryClient();
const theme = useMantineTheme();
const colorScheme = useColorScheme();
- const projects = useCanvasStore((state) => state.projects);
- const selectedProject = useCanvasStore((state) => state.selectedProject);
-
- const { data: currentUser } = useQuery({
- queryKey: ["currentUser"],
- queryFn: () => getCurrentUser(),
- });
-
- const form = useForm({
- initialValues: {
- projectId: selectedProject?.id,
- type: "",
- sprint: { id: undefined as unknown as number },
- summary: "",
- description: "",
- assignee: { id: "" },
- status: "To Do",
- reporter: currentUser,
- priority: { id: "" },
- epic: { issueKey: undefined },
- } as Issue,
- });
-
- const { data: issueTypes, isLoading } = useQuery({
- queryKey: ["issueTypes", form.getInputProps("projectId").value],
- queryFn: () => getIssueTypes(form.getInputProps("projectId").value!),
- enabled: !!projects && !!form.getInputProps("projectId").value,
- });
-
- const { data: assignableUsers } = useQuery({
- queryKey: ["assignableUsers", form.getInputProps("projectId").value],
- queryFn: () => {
- const relevantProject = projects.find(
- (project) => project.id === form.getInputProps("projectId").value!,
- )!;
-
- return getAssignableUsersByProject(relevantProject.key);
- },
- enabled: !!projects && !!form.getInputProps("projectId").value,
- });
-
- const { data: issueTypesWithFieldsMap } = useQuery({
- queryKey: ["issueTypesWithFieldsMap"],
- queryFn: () => getIssueTypesWithFieldsMap(),
- });
-
- const mutation = useMutation({
- mutationFn: (issue: Issue) => createIssue(issue),
- onError: () => {
- showNotification({
- message: "The issue couldn't be created! 😢",
- color: "red",
- });
- },
- onSuccess: (issueKey) => {
- const files: File[] = form.getInputProps("attachment").value;
- const filesForm = new FormData();
- if (files) {
- files.forEach((f) => filesForm.append("file", f, f.name));
- getResource().then((r) => uploadAttachment(issueKey, r, filesForm));
- }
- showNotification({
- message: `The issue ${issueKey} has been created!`,
- color: "green",
- });
- queryClient.invalidateQueries({ queryKey: ["issues"] });
- queryClient.invalidateQueries({ queryKey: ["epics"] });
- setOpened(false);
- form.reset();
- },
- });
return (
setOpened(false)}
+ onClose={onCancel}
title="Create Issue"
size="70vw"
overlayProps={{
- color:
- colorScheme === "dark" ? theme.colors.dark[9] : theme.colors.gray[2],
+ color: colorScheme === "dark" ? theme.colors.dark[9] : theme.colors.gray[2],
opacity: 0.55,
blur: 5,
}}
>
-
-
-
-
+
);
}
diff --git a/src/components/CreateIssue/CreateIssueModalContent.tsx b/src/components/CreateIssue/CreateIssueModalContent.tsx
new file mode 100644
index 00000000..c4ecaf2d
--- /dev/null
+++ b/src/components/CreateIssue/CreateIssueModalContent.tsx
@@ -0,0 +1,179 @@
+import { Button, Divider, Group, ScrollArea, Stack } from "@mantine/core";
+import { useForm } from "@mantine/form";
+import { showNotification } from "@mantine/notifications";
+import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
+import { Issue } from "types";
+import { useCanvasStore } from "../../lib/Store";
+import { getResource, uploadAttachment } from "../DetailView/Components/Attachments/queryFunctions";
+import {
+ ProjectSelect,
+ IssueTypeSelect,
+ StatusSelect,
+ SummaryInput,
+ DescriptionInput,
+ AssigneeSelect,
+ PrioritySelect,
+ SprintSelect,
+ EpicSelect,
+ StoryPointsEstimateInput,
+ ReporterSelect,
+ StartDatePicker,
+ DueDatePicker,
+ LabelsSelect,
+ AttachmentFileInput,
+} from "./Fields";
+
+export function CreateIssueModalContent({
+ onCancel,
+ onCreate,
+}: {
+ onCancel: () => void,
+ onCreate: () => void,
+}) {
+ const queryClient = useQueryClient();
+ const { projects, selectedProject, issueTypesWithFieldsMap } = useCanvasStore();
+
+ const { data: currentUser } = useQuery({
+ queryKey: ["currentUser"],
+ queryFn: () => window.provider.getCurrentUser(),
+ });
+
+ const form = useForm({
+ initialValues: {
+ projectId: selectedProject?.key,
+ type: "",
+ sprint: { id: undefined as unknown as number },
+ summary: "",
+ description: "",
+ assignee: { id: "" },
+ status: "To Do",
+ reporter: currentUser,
+ priority: { id: "" },
+ epic: { issueKey: undefined },
+ } as Issue,
+ });
+
+ const { data: issueTypes, isLoading } = useQuery({
+ queryKey: ["issueTypes", form.getInputProps("projectId").value],
+ queryFn: () => window.provider.getIssueTypesByProject(form.getInputProps("projectId").value!),
+ enabled: !!projects && !!form.getInputProps("projectId").value,
+ });
+
+ const { data: assignableUsers } = useQuery({
+ queryKey: ["assignableUsers", form.getInputProps("projectId").value],
+ queryFn: () => window.provider.getAssignableUsersByProject(form.getInputProps("projectId").value!),
+ enabled: !!projects && !!form.getInputProps("projectId").value,
+ });
+
+ const mutation = useMutation({
+ mutationFn: (issue: Issue) => window.provider.createIssue(issue),
+ onError: () => {
+ showNotification({
+ message: "The issue couldn't be created! 😢",
+ color: "red",
+ });
+ },
+ onSuccess: (issueKey) => {
+ const files: File[] = form.getInputProps("attachment").value;
+ const filesForm = new FormData();
+ if (files) {
+ files.forEach((f) => filesForm.append("file", f, f.name));
+ getResource().then((r) => uploadAttachment(issueKey, r, filesForm));
+ }
+ showNotification({
+ message: `The issue ${issueKey} has been created!`,
+ color: "green",
+ });
+ queryClient.invalidateQueries({ queryKey: ["issues"] });
+ queryClient.invalidateQueries({ queryKey: ["epics"] });
+ onCreate();
+ form.reset();
+ },
+ });
+
+ return (
+
+
+
+ );
+}
diff --git a/src/components/CreateIssue/Fields/EpicSelect.tsx b/src/components/CreateIssue/Fields/EpicSelect.tsx
index 7ae86e26..0aa535dc 100644
--- a/src/components/CreateIssue/Fields/EpicSelect.tsx
+++ b/src/components/CreateIssue/Fields/EpicSelect.tsx
@@ -2,7 +2,6 @@ import { Select, Tooltip, Box } from "@mantine/core";
import { UseFormReturnType } from "@mantine/form";
import { useQuery } from "@tanstack/react-query";
import { Issue, IssueType } from "types";
-import { getEpicsByProject } from "../queryFunctions";
export function EpicSelect({
form,
@@ -20,7 +19,7 @@ export function EpicSelect({
}) {
const { data: epics } = useQuery({
queryKey: ["epics", form.getInputProps("projectId").value],
- queryFn: () => getEpicsByProject(form.getInputProps("projectId").value!),
+ queryFn: () => window.provider.getEpicsByProject(form.getInputProps("projectId").value!),
enabled: enabled && !!form.getInputProps("projectId").value,
});
diff --git a/src/components/CreateIssue/Fields/LabelsSelect.tsx b/src/components/CreateIssue/Fields/LabelsSelect.tsx
index 81332e85..b2ecfa1a 100644
--- a/src/components/CreateIssue/Fields/LabelsSelect.tsx
+++ b/src/components/CreateIssue/Fields/LabelsSelect.tsx
@@ -2,12 +2,11 @@ import { MultiSelect } from "@mantine/core";
import { UseFormReturnType } from "@mantine/form";
import { useQuery } from "@tanstack/react-query";
import { Issue } from "types";
-import { getLabels } from "../queryFunctions";
export function LabelsSelect({ form }: { form: UseFormReturnType }) {
const { data: labels } = useQuery({
queryKey: ["labels"],
- queryFn: () => getLabels(),
+ queryFn: () => window.provider.getLabels(),
});
return (
diff --git a/src/components/CreateIssue/Fields/PrioritySelect.tsx b/src/components/CreateIssue/Fields/PrioritySelect.tsx
index e5249039..05138cfd 100644
--- a/src/components/CreateIssue/Fields/PrioritySelect.tsx
+++ b/src/components/CreateIssue/Fields/PrioritySelect.tsx
@@ -2,7 +2,6 @@ import { Box, Tooltip } from "@mantine/core";
import { UseFormReturnType } from "@mantine/form";
import { useQuery } from "@tanstack/react-query";
import { Issue } from "types";
-import { getPriorities } from "../queryFunctions";
import { SelectItem } from "../SelectItem";
import { CustomItemSelect } from "../../common/CustomItemSelect";
@@ -17,7 +16,7 @@ export function PrioritySelect({
}) {
const { data: priorities } = useQuery({
queryKey: ["priorities"],
- queryFn: () => getPriorities(),
+ queryFn: () => window.provider.getPriorities(),
});
const isDisabled = issueTypesWithFieldsMap
diff --git a/src/components/CreateIssue/Fields/SprintSelect.tsx b/src/components/CreateIssue/Fields/SprintSelect.tsx
index c2e4cbc3..3fd68b96 100644
--- a/src/components/CreateIssue/Fields/SprintSelect.tsx
+++ b/src/components/CreateIssue/Fields/SprintSelect.tsx
@@ -2,7 +2,6 @@ import { Select, Tooltip, Box } from "@mantine/core";
import { UseFormReturnType } from "@mantine/form";
import { useQuery } from "@tanstack/react-query";
import { Issue, IssueType } from "types";
-import { getBoardIds, getSprints } from "../queryFunctions";
export function SprintSelect({
form,
@@ -19,13 +18,13 @@ export function SprintSelect({
}) {
const { data: boardIds } = useQuery({
queryKey: ["boards", form.getInputProps("projectId").value],
- queryFn: () => getBoardIds(form.getInputProps("projectId").value!),
+ queryFn: () => window.provider.getBoardIds(form.getInputProps("projectId").value!),
enabled: enabled && !!form.getInputProps("projectId").value,
});
const { data: sprints } = useQuery({
queryKey: ["sprints"],
// TODO: fetch when boards are fetched (iterate over all boards) or select a specific one
- queryFn: () => getSprints(boardIds![0]),
+ queryFn: () => window.provider.getSprints(boardIds![0]),
enabled: enabled && !!boardIds && !!boardIds[0],
});
diff --git a/src/components/CreateIssue/queryFunctions.ts b/src/components/CreateIssue/queryFunctions.ts
deleted file mode 100644
index 7fb97b55..00000000
--- a/src/components/CreateIssue/queryFunctions.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Issue, IssueType, Priority, Sprint, User } from "types";
-
-export const getIssueTypes = (projectIdOrKey: string): Promise => window.provider.getIssueTypesByProject(projectIdOrKey);
-
-export const createIssue = (issue: Issue): Promise => window.provider.createIssue(issue);
-
-export const moveIssueToBacklog = (issueIdOrKey: string): Promise => window.provider.moveIssueToBacklog(issueIdOrKey);
-
-export const getAssignableUsersByProject = (
- projectIdOrKey: string,
-): Promise => window.provider.getAssignableUsersByProject(projectIdOrKey);
-
-export const getBoardIds = (projectIdOrKey: string): Promise => window.provider.getBoardIds(projectIdOrKey);
-
-export const getSprints = (boardId: number): Promise => window.provider.getSprints(boardId);
-
-export const getLabels = (): Promise => window.provider.getLabels();
-
-export const getCurrentUser = (): Promise => window.provider.getCurrentUser();
-
-export const getPriorities = (): Promise => window.provider.getPriorities();
-
-export const getIssueTypesWithFieldsMap = (): Promise