0% found this document useful (0 votes)
30 views17 pages

Issue Comments and Management System

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
30 views17 pages

Issue Comments and Management System

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

comments:

/* eslint-disable @typescript-eslint/no-explicit-any */
import React from "react";
import { useAuth } from "@/lib/contexts/AuthContext";
import { Comment, Issue } from "./IssuesMain";

export default function Comments({


allComments,
issue,
lastMessageRef,
}: {
allComments: Array<Comment>;
issue: Issue;
lastMessageRef: any;
}) {
const { user } = useAuth();
return (
<div>
<div className="shadow-md rounded-t-0 px-2 py-1">
<div className="flex justify-between">
<h1 className="font-semibold">{issue?.issueName}</h1>
<p>{issue.createdAt?.slice(0, 10)}</p>
</div>
<p>{issue.description}</p>
</div>
<div className="min-h-[50vh] max-h-[50vh] overflow-x-hidden overflow-y-
scroll">
{allComments.length > 0 ? (
allComments.map((comment: Comment) => {
const commentCss = {
alignment:
user._id === comment.senderId
? "justify-end"
: "justify-start",
bgColor:
user._id === comment.senderId
? "bg-blue-500"
: "bg-green-500",
};
return (
<div key={comment._id} className="mt-5">
<div className="space-y-3">
<div
className={`flex ${commentCss.alignment}`}
>
<div className="max-w-[450px] relative">
<p
className={` ${commentCss.bgColor}
rounded-md mx-2 pr-7 px-2 py-2 text-white max-w-[400px] `}
>
{comment.comment}
</p>

<p className="px-2 absolute bottom-0


right-1.5 text-[10px] text-end text-white">
{comment.createdAt.slice(
12,
16
)}
</p>
</div>
</div>
</div>
<div ref={lastMessageRef} />
</div>
);
})
) : (
<p className="text-black text-center p-5">
No comments found
</p>
)}
</div>
</div>
);
}

createcomments:

/* eslint-disable @typescript-eslint/no-explicit-any */
import { Form, Formik } from "formik";
import React from "react";
import TextInput1 from "../common/Input/TextInput1";
import { useMutation } from "react-query";
import { CREATE_ISSUE_COMMENT } from "@/lib/ReactQueryKeys";
import useHttpClient from "@/lib/hooks/useHttpClient";
import { usePopup } from "@/lib/contexts/PopupContext";

interface Comment {
comment: string;
}

export default function CreateComment({


scrollBottomMessage,
issueId,
}: {
scrollBottomMessage: any;
issueId: string;
}) {
const axios = useHttpClient();
const { showError } = usePopup();
const { mutate: postComment } = useMutation(
CREATE_ISSUE_COMMENT.key,
(values: Comment) =>
axios.post(`${CREATE_ISSUE_COMMENT.path}/${issueId}`, values),
{
onError: ({
response: {
data: { message },
},
}) => {
showError(message);
},
}
);
return (
<Formik
initialValues={{
comment: "",
}}
onSubmit={(values: Comment, { setSubmitting, resetForm }) => {
postComment(values);
setSubmitting(false);
resetForm();
}}
>
{({ isSubmitting, values }) => (
<Form>
<div className="p-5 border-t-2 rounded-br-lg-lg rounded-bl-lg">
<div className="flex w-[500px] lg:w-[700px] mx-auto space-
x-4">
<TextInput1
name="comment"
inputstyle=" min-w-[300px] lg:min-w-[500px]"
/>
<button
type="submit"
disabled={isSubmitting || !values.comment}
className={`bg-red-500 px-4 text-white bg-
primary-500 border-primary-800 border-[1px] rounded-md`}
>
Send
</button>
<button
className="border px-4 rounded-lg"
type="button"
onClick={() => scrollBottomMessage()}
>
Scroll
</button>
</div>
</div>
</Form>
)}
</Formik>
);
}

issueslist:

import React from "react";


import { Issue } from "./IssuesMain";

export default function IssuesList({


issues,
setIssue,
issue,
}: {
issues: Array<Issue>;
setIssue: React.Dispatch<React.SetStateAction<Issue>>;
issue: Issue;
}) {
return (
<div className="flex md:block overflow-y-scroll overflow-x-hidden gap-8
md:gap-0 ">
{issues.map((det: Issue, idx: number) => (
<div
key={det._id}
onClick={() => setIssue(det)}
className={`border-b-[1px] w-[300px] hover:cursor-pointer
border-primary-600 p-2 ${
issue._id === det._id && "bg-zinc-300"
} ${idx === 0 && "rounded-tl-lg"}`}
>
<p className="text-sm font-medium">
{idx + 1}
{" . "}
{det.issueName}
</p>
{/* <p className="text-sm font-medium">{det.description}</p>
*/}
<p className="text-grayScale-800 text-sm">
{det.createdAt?.slice(0, 10)}
</p>
</div>
))}
</div>
);
}

issuesmain:

import React, { useState, useEffect, useRef, useCallback } from "react";


import { useMutation, useQuery } from "react-query";
import useHttpClient from "../../lib/hooks/useHttpClient";
import {
CREATE_ISSUE,
GET_ISSUES,
GET_ISSUE_COMMENTS,
} from "@/lib/ReactQueryKeys";
import { usePopup } from "@/lib/contexts/PopupContext";
import CreateIssue from "./CreateIssue";
import { useAuth } from "@/lib/contexts/AuthContext";
import IssuesList from "./IssuesList";
import { useSocketContext } from "@/lib/contexts/SocketContext";
import Comments from "./Comments";
import CreateComment from "./CreateComment";

export interface Comment {


senderId: string;
issueId: string;
comment: string;
createdAt: string;
_id: string;
}

export interface Issue {


issueName: string;
description: string;
_id: string;
createdAt: string;
}

export default function IssuesMain() {


const { showSuccess, showError } = usePopup();
const [issue, setIssue] = useState<Issue>({
issueName: "",
description: "",
_id: "",
createdAt: "",
});
const [allComments, setAllComments] = useState<Array<Comment>>([]);
const { accessToken } = useAuth();
const [createIssue, setCreateIssue] = useState<boolean>(false);
const axios = useHttpClient();
const { joinSocket, socket } = useSocketContext();
const lastMessageRef = useRef<HTMLDivElement>(null);

const scrollBottomMessage = () => {


lastMessageRef.current?.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "start",
});
};

useEffect(() => {
if (issue._id) joinSocket(issue._id, accessToken);
if (socket) {
socketListenComments();
}
scrollBottomMessage();
}, [issue._id, socket, allComments.length]);
const { data: { data: issues = [] } = {}, refetch: issueRefetch } =
useQuery(GET_ISSUES.key, () => axios.get(GET_ISSUES.path), {
onSuccess: ({ data: issues }) => {
setIssue(issues[0]);
},
enabled: !issue._id,
refetchOnMount: false,
refetchOnWindowFocus: false,
});
useQuery(
[GET_ISSUE_COMMENTS.key, issue._id],
() => axios.get(`${GET_ISSUE_COMMENTS.path}/${issue._id}`),
{
onSuccess: ({ data: comments }) => {
setAllComments(comments);
},
enabled: !!issue._id,
refetchOnMount: false,
refetchOnWindowFocus: false,
}
);

const socketListenComments = useCallback(() => {


socket?.on("new-comment", (comment: Comment) => {
setAllComments([...allComments, comment]);
});
return () => socket?.off("new-comment");
}, [socket, allComments.length]);

const { mutate: postIssue, isLoading: issueLoading } = useMutation(


CREATE_ISSUE.key,
(values: Issue) => axios.post(CREATE_ISSUE.path, values),
{
onSuccess: ({ data: { message = "" } = {} }) => {
showSuccess(message);
setCreateIssue(false);
issueRefetch();
},
onError: ({
response: {
data: { message },
},
}) => {
showError(message);
},
}
);

return (
<div className="p-5 bg-lightBG-60">
<div className="flex justify-between">
<h1 className="font-bold text-xl ml-3">Issues</h1>
<button
onClick={() => setCreateIssue(true)}
className="px-2 py-1 border-2 bg-red-600 text-white rounded-md"
>
Create Issue
</button>
</div>
{issues.length > 0 ? (
<div className="md:flex h-[490px] max-w-[1200px] rounded-xl shadow-
lg border-2 mt-8">
<IssuesList
issues={issues}
setIssue={setIssue}
issue={issue}
/>
<div className="w-full border-l-2 rounded-tr-lg rounded-br-lg
border-primary-600">
<Comments
lastMessageRef={lastMessageRef}
issue={issue}
allComments={allComments}
/>
<CreateComment
issueId={issue._id}
scrollBottomMessage={scrollBottomMessage}
/>
</div>
</div>
) : (
<div className="ml-3 mt-2 text-lg text-center text-primary-100">
Issues not Found
</div>
)}
{createIssue && (
<div className="max-h-screen absolute bg-opacity-50 flex justify-
center bg-primary-1000 inset-0 z-10">
<CreateIssue
setCreateIssue={setCreateIssue}
postIssue={postIssue}
issueLoading={issueLoading}
/>
</div>
)}
</div>
);
}

createIssues:

/* eslint-disable @typescript-eslint/no-explicit-any */
import { Form, Formik } from "formik";
import React from "react";
import TextInput1 from "../common/Input/TextInput1";
import LittleLoader from "../common/LittleLoader";
import * as Yup from "yup";

const issueSchema = Yup.object().shape({


issueName: Yup.string().required("Required"),
description: Yup.string().required("Required"),
});

interface Issue {
issueName: string;
description: string;
}

export default function CreateIssue({


postIssue,
issueLoading,
setCreateIssue,
}: {
postIssue: any;
setCreateIssue: React.Dispatch<React.SetStateAction<boolean>>;
issueLoading: boolean;
}) {
return (
<div className="w-full border shadow-md rounded-md min-h-full bg-black bg-
opacity-50">
<Formik
initialValues={{
issueName: "",
description: "",
}}
validationSchema={issueSchema}
onSubmit={(values: Issue, { setSubmitting }) => {
postIssue(values);
setSubmitting(false);
}}
>
{({ isSubmitting }) => (
<div className="bg-white mt-[150px] mx-auto w-[400px] rounded-
md shadow-md">
<h1 className="text-black text-xl font-semibold py-5 text-
center">
Create Issue
</h1>
<div className="flex justify-center">
<Form>
<div className="">
<div className="space-y-2">
<div className="">
<TextInput1
name="issueName"
inputstyle="w-[350px]"
/>
</div>
<div className="">
<TextInput1 name="description" />
</div>
</div>
<div className="flex gap-2 justify-end mt-2
pb-4">
<button
type="button"
onClick={() =>
setCreateIssue(false)
}
className="w-[96px] h-[36px] text-
primary-500 bg-white border-primary-500 border-[1px] rounded-sm"
>
Back
</button>
<button
type="submit"
disabled={
isSubmitting || issueLoading
}
className=" px-4 text-red-500 bg-
primary-500 border-primary-800 border-[1px] rounded-sm"
>
{issueLoading ? (
<LittleLoader />
) : (
"Submit"
)}
</button>
</div>
</div>
</Form>
</div>
</div>
)}
</Formik>
</div>
);
}

index.tsx:

import IssuesMain from "@/components/Issue/IssuesMain";


import React from "react";
export default function Index() {
return (
<div>
<IssuesMain />
</div>
);
}

export const GET_ISSUE_COMMENTS = {


key: "get-issue-comments",
path: "api/issue/comment"
}

"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NjVhZjU2ZmE0M2ViZGU5YTMwZTQ3YmIiL
CJuYW1lIjoiQWtzaGl0aGEgUmF2aWNoYW5kcmFuIiwiZW1haWwiOiJha3NoaXRoYXJhdmljaGFuZHJhbjE3
QGdtYWlsLmNvbSIsInJvbGVzIjpbM10sImlhdCI6MTcxNzQwMTg4OSwiZXhwIjoxNzE3NDg4Mjg5fQ.xHbc
cM_qh-13_dQmKYcwB39jNWUBRH06SdSxkgGqKsI",

Comments:
import React, { useEffect, useRef } from "react";
import { useQuery, useMutation, useQueryClient } from "react-query";
import axios from "axios";
import { io, Socket } from "socket.io-client";
import { useAuth } from '@/lib/contexts/AuthContext';
import useHttpClient from "@/lib/hooks/useHttpClient";
import { usePopup } from "@/lib/contexts/PopupContext";

const axiosInstance = axios.create({


baseURL: 'http://18.142.234.81/ncer',
headers: {
'Content-Type': 'application/json',
},
});

const fetchComments = async (issueId: string, token: string) => {


const response = await axiosInstance.get(`/issue/send-comment`, {
headers: {
Authorization: `Bearer ${token}`
}
});
if (response.status !== 200) {
throw new Error("Failed to fetch comments");
}
return response.data;
};

const postComment = async ({ issueId, message, token }: { issueId: string; message:


string; token: string }) => {
const response = await axiosInstance.post(`/api/issue/send-comment`,
{ message },
{
headers: {
Authorization: `Bearer ${token}`
}
});
if (response.status !== 200) {
throw new Error("Failed to post comment");
}
return response.data;
};

interface Comment {
id: string;
senderName: string;
comment: string;
createdAt: string;
}

export default function CommentsSection({ issueId }: { issueId: string }) {


const { user, accessToken } = useAuth(); // Ensure accessToken is available
const axios = useHttpClient();
const queryClient = useQueryClient();
const { showError } = usePopup();
const commentInputRef = useRef<HTMLTextAreaElement | null>(null);
const lastMessageRef = useRef<HTMLDivElement | null>(null);
const socket = useRef<Socket | null>(null);

const { data: comments = [], error } = useQuery<Comment[]>(


["comments", issueId],
() => fetchComments(issueId, accessToken), // Pass accessToken here
{
refetchInterval: 3000,
}
);

const addCommentMutation = useMutation(postComment, {


onSuccess: () => {
queryClient.invalidateQueries(["comments", issueId]);
scrollToBottom();
},
});

useEffect(() => {
if (!accessToken) return;

socket.current = io("http://localhost:3000", {
query: {
accessToken,
},
});

socket.current.on("connect", () => {
console.log("Socket connected");
socket.current?.emit("join", { issueId });
});

socket.current.on("new-comment", () => {
console.log("Received new comment");
queryClient.invalidateQueries(["comments", issueId]);
scrollToBottom();
});

socket.current.on("disconnect", () => {
console.log("Socket disconnected");
});

socket.current.on("error", (err) => {


console.error("Socket error:", err);
});

return () => {
socket.current?.disconnect();
};
}, [issueId, queryClient, accessToken]);

const scrollToBottom = () => {


if (lastMessageRef.current) {
lastMessageRef.current.scrollIntoView({ behavior: "smooth" });
}
};

const handlePostComment = () => {


const comment = commentInputRef.current?.value;
if (comment && comment.trim() !== "") {
addCommentMutation.mutate({ issueId, message: comment, token:
accessToken }); // Pass accessToken here
if (commentInputRef.current) {
commentInputRef.current.value = "";
}
}
};

if (error) {
return <div>Error fetching comments</div>;
}

return (
<div className="mx-auto max-w-2xl rounded-lg bg-white p-6 shadow-md">
<h2 className="mb-6 text-2xl font-semibold">Comments</h2>

<div className="mb-6">
<textarea
ref={commentInputRef}
className="w-full rounded-lg bg-gray-100 p-4 text-gray-700
focus:border-blue-500 focus:bg-white focus:outline-none"
rows={4}
placeholder="Write a comment..."
></textarea>
<div className="mt-2 flex justify-end">
<button
onClick={handlePostComment}
className="rounded-lg bg-blue-500 px-4 py-2 text-white
hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-
opacity-50"
>
Post Comment
</button>
</div>
</div>

<div className="space-y-6">
{comments.length > 0 ? (
comments.map((comment) => (
<div key={comment.id} className="flex space-x-4">
<div>
<img
src="https://via.placeholder.com/50"
alt="User Avatar"
className="h-12 w-12 rounded-full"
/>
</div>
<div>
<div className="rounded-lg bg-gray-100 p-4">
<div className="mb-2 flex items-center justify-
between">
<h3 className="font-
semibold">{comment.senderName}</h3>
<span className="text-sm text-gray-
500">{new Date(comment.createdAt).toLocaleString()}</span>
</div>
<p className="text-gray-
700">{comment.comment}</p>
</div>

</div>
</div>
))
) : (
<p>No comments found</p>
)}
<div ref={lastMessageRef} />
</div>
</div>
);
}
{/* Issue Header */}
{/* <div className="bg-blue-500 text-white px-6 py-4 rounded-t-lg">
<h2 className="text-xl font-semibold">{issue?.issueName}</h2>
<p className="text-sm text-gray-200">{issue.createdAt?.slice(0,
10)}</p>
<p className="mt-2 text-gray-100">{issue.description}</p>
</div> */}

createcomments.tsx:
import { Form, Formik } from "formik";
import React from "react";
import TextInput1 from "../common/Input/TextInput1";
import { useMutation } from "react-query";
import { CREATE_ISSUE_COMMENT } from "@/lib/ReactQueryKeys";
import useHttpClient from "@/lib/hooks/useHttpClient";
import { usePopup } from "@/lib/contexts/PopupContext";

interface Comment {
comment: string;
}

export default function CreateComment({

issueId,
}: {
scrollBottomMessage: any;
issueId: string;
}) {
const axios = useHttpClient();
const { showError } = usePopup();
const { mutate: postComment } = useMutation(
CREATE_ISSUE_COMMENT.key,
(values: Comment) =>
axios.post(`${CREATE_ISSUE_COMMENT.path}/${issueId}`, values),
{
onError: ({
response: {
data: { message },
},
}) => {
showError(message);
},
}
);

return (
<Formik
initialValues={{
comment: "",
}}
onSubmit={(values: Comment, { setSubmitting, resetForm }) => {
postComment(values);
setSubmitting(false);
resetForm();
}}
>
{({ isSubmitting, values }) => (
<Form>
<div className="p-5 border-t-2 rounded-br-lg rounded-bl-lg bg-
white shadow-md">
<div className="flex w-full space-x-4">
<TextInput1
name="comment"
inputstyle="min-w-[300px] lg:min-w-[400px] w-full
rounded-lg bg-gray-100 p-4 text-gray-700 focus:border-blue-500 focus:bg-white
focus:outline-none"
placeholder="Write a comment..."
/>
<button
type="submit"
disabled={isSubmitting || !values.comment}
className="rounded-lg bg-blue-500 px-4 py-2 text-
white hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500
focus:ring-opacity-50"
>
Post Comment
</button>

</div>
</div>
</Form>
)}
</Formik>
);
}
commments.tsx:
import React from "react";
import { useAuth } from "@/lib/contexts/AuthContext";
import { Comment, Issue } from "./IssuesMain";
interface CommentsProps {
allComments: Array<Comment>;
issue: Issue;
lastMessageRef: any;
}

const Comments: React.FC<CommentsProps> = ({ allComments, issue, lastMessageRef })


=> {
const { user } = useAuth();

return (
<div className="max-w-2xl mx-auto mt-6 p-4 bg-white shadow-md rounded-lg">
<div className="overflow-y-auto max-h-[300px] mt-4">
{allComments.length > 0 ? (
allComments.map((comment: Comment) => {
const isCurrentUser = user._id === comment.senderId;
const commentCss = isCurrentUser
? "bg-blue-500 text-white self-end "
: "bg-white text-gray-800 border border-gray-300 self-
start shadow";

return (
<div
key={comment._id}
className={`flex flex-col items-start mb-4 $
{isCurrentUser && "self-end"}`}
>

<div
className={`rounded-lg py-2 px-4 max-w-[70%]
text-sm break-words ${commentCss}`}
>
<div className="mb-2 flex items-center justify-
between">
<h3 className="font-semibold">User</h3>

</div>
<p className="text-base">{comment.comment}</p>

<span className="text-xs text-gray-600">


{new
Date(comment.createdAt).toLocaleString()}
</span>
</div>
</div>
);
})
) : (
<p className="text-gray-500 text-center mt-4">No comments
found</p>
)}
<div ref={lastMessageRef} />
</div>
</div>
);
};

export default Comments;


issuesMain
import React, { useState, useEffect, useRef, useCallback } from "react";
import { useMutation, useQuery } from "react-query";
import useHttpClient from "../../lib/hooks/useHttpClient";
import { usePopup } from "@/lib/contexts/PopupContext";
import { useAuth } from "@/lib/contexts/AuthContext";
import { useSocketContext } from "@/lib/contexts/SocketContext";
import Comments from "./Comments";
import {
CREATE_ISSUE,
GET_ISSUES,
GET_ISSUE_COMMENTS,
} from "@/lib/ReactQueryKeys";
import PostComment from "./PostComment";
import CreateIssues from "./CreateIssues";

export interface Comment {


senderId: string;
issueId: string;
comment: string;
createdAt: string;
_id: string;
}

export interface Issue {


issueName: string;
description: string;
_id: string;
createdAt: string;
}

export default function IssuesMain() {


const { showSuccess, showError } = usePopup();
const [issue, setIssue] = useState<Issue>({
issueName: "",
description: "",
_id: "",
createdAt: "",
});
const [allComments, setAllComments] = useState<Array<Comment>>([]);
const { accessToken } = useAuth();
const [createIssue, setCreateIssue] = useState<boolean>(false);
const axios = useHttpClient();
const { joinSocket, socket } = useSocketContext();
const lastMessageRef = useRef<HTMLDivElement>(null);

const scrollBottomMessage = () => {


lastMessageRef.current?.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "start",
});
};

useEffect(() => {
if (issue._id && accessToken && typeof joinSocket === "function") {
joinSocket(issue._id, accessToken);
}
if (socket) {
socketListenComments();
}
scrollBottomMessage();
}, [issue._id, socket, allComments.length]);

const { data: { data: issues = [] } = {}, refetch: issueRefetch } =


useQuery(GET_ISSUES.key, () => axios.get(GET_ISSUES.path), {
onSuccess: ({ data: issues }) => {
setIssue(issues[0]);
},
enabled: !issue._id,
refetchOnMount: false,
refetchOnWindowFocus: false,
});

useQuery(
[GET_ISSUE_COMMENTS.key, issue._id],
() => axios.get(`${GET_ISSUE_COMMENTS.path}/${issue._id}`),
{
onSuccess: ({ data: comments }) => {
setAllComments(comments);
},
enabled: !!issue._id,
refetchOnMount: false,
refetchOnWindowFocus: false,
}
);

const socketListenComments = useCallback(() => {


socket?.on("new-comment", (comment: Comment) => {
setAllComments((prevComments) => [...prevComments, comment]);
});
return () => socket?.off("new-comment");
}, [socket]);

const { mutate: postIssue, isLoading: issueLoading } = useMutation(


CREATE_ISSUE.key,
(values: Issue) => axios.post(CREATE_ISSUE.path, values),
{
onSuccess: ({ data: { message = "" } = {} }) => {
showSuccess(message);
setCreateIssue(false);
issueRefetch();
},
onError: ({
response: {
data: { message },
},
}) => {
showError(message);
},
}
);

return (
<div className="p-5 bg-lightBG-60">
<div className="flex justify-between">
<h1 className="font-bold text-xl ml-3">Issues</h1>
<button
onClick={() => setCreateIssue(true)}
className="px-2 py-1 border-2 bg-red-600 text-white rounded-md"
>
Create Issue
</button>
</div>
{issues.length > 0 ? (
<div className="md:flex h-[490px] max-w-[1200px] rounded-xl shadow-
lg border-2 mt-8">
<div className="w-full border-l-2 rounded-tr-lg rounded-br-lg
border-primary-600">
<PostComment
issueId={issue._id}
scrollBottomMessage={scrollBottomMessage}
/>
<Comments
lastMessageRef={lastMessageRef}
issue={issue}
allComments={allComments}
/>
</div>
</div>
) : (
<div className="ml-3 mt-2 text-lg text-center text-primary-100">
Issues not Found
</div>
)}
{createIssue && (
<div className="max-h-screen absolute bg-opacity-50 flex justify-
center bg-primary-1000 inset-0 z-10">
<CreateIssues
setCreateIssue={setCreateIssue}
postIssue={postIssue}
issueLoading={issueLoading}
/>
</div>
)}
</div>
);
}

You might also like