From e3084ac8041b88ae28ddbaf8cd4392716583c66b Mon Sep 17 00:00:00 2001 From: Pranshu Aggarwal <70687348+Pranshu1902@users.noreply.github.com> Date: Tue, 3 Oct 2023 14:14:48 +0530 Subject: [PATCH] use modal before deleting (#126) --- src/app/admin/tests/[testsuite_id]/page.tsx | 1149 +++++++++++-------- 1 file changed, 691 insertions(+), 458 deletions(-) diff --git a/src/app/admin/tests/[testsuite_id]/page.tsx b/src/app/admin/tests/[testsuite_id]/page.tsx index 8f8e57f8..ceae97f6 100644 --- a/src/app/admin/tests/[testsuite_id]/page.tsx +++ b/src/app/admin/tests/[testsuite_id]/page.tsx @@ -7,498 +7,731 @@ import Modal from "@/components/modal"; import { Button, Input, TextArea } from "@/components/ui/interactive"; import { Document, Project } from "@/types/project"; import { API } from "@/utils/api"; -import { QueryFunction, useInfiniteQuery, useMutation, useQuery } from "@tanstack/react-query"; +import { + QueryFunction, + useInfiniteQuery, + useMutation, + useQuery, +} from "@tanstack/react-query"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Toaster, toast } from "react-hot-toast"; -import { TestSuite, TestQuestion, TestRun, TestResult, TestRunStatus } from "@/types/test"; +import { + TestSuite, + TestQuestion, + TestRun, + TestResult, + TestRunStatus, +} from "@/types/test"; export default function Page({ params }: { params: { testsuite_id: string } }) { - const router = useRouter(); - const { testsuite_id } = params; - - const testSuiteQuery = useQuery(["testsuite", testsuite_id], () => API.tests.suites.get(testsuite_id)); - const testSuite: TestSuite | undefined = testSuiteQuery.data || undefined; - - const TestQuestionsQuery = useQuery(["testsuitequestion", testsuite_id], () => API.tests.questions.list(testsuite_id, { ordering: "created_at" })); - const testQuestions: TestQuestion[] | undefined = TestQuestionsQuery.data?.results || undefined; - - const ProjectListQuery = useQuery(["projects"], () => API.projects.list()); - const projects: Project[] = ProjectListQuery.data?.results || []; - - type APIResponse = { - results: TestRun[]; - offset: number | null; - } - - const fetchData: QueryFunction = async ({ - pageParam, - }) => { - const offset = pageParam ? pageParam : 0; - const res = await API.tests.runs.list(testsuite_id, { ordering: "-created_at", offset: offset, limit: 10 }); - const testRuns: TestRun[] = res.results; - return { - results: testRuns, - offset: offset + 10, - }; - }; - - const { - data, - fetchNextPage, - hasNextPage, - isFetching, - isLoading, - refetch, - } = useInfiniteQuery({ - queryKey: ["testruns"], - queryFn: fetchData, - getNextPageParam: (lastPage, pages) => { return lastPage.results.length > 0 ? lastPage.offset : null }, - }); - - useEffect(() => { - if (data?.pages.flatMap(item => item.results).find(item => item.status === TestRunStatus.RUNNING)) { - setTimeout(() => { - refetch(); - }, 5000); - } - }, [data, refetch]); - - const testRuns = useMemo( - () => (data ? data?.pages.flatMap(item => item.results) : []), - [data] - ); - - const observer = useRef(); - const lastElementRef = useCallback( - (node: HTMLButtonElement) => { - if (isLoading) return; - if (observer.current) observer.current.disconnect(); - observer.current = new IntersectionObserver(entries => { - if (entries[0].isIntersecting && hasNextPage && !isFetching) { - fetchNextPage(); - } - }); - if (node) observer.current.observe(node); - }, - [isLoading, hasNextPage, fetchNextPage, isFetching] - ); - - const TestQuestionsAddMutation = useMutation((question: Partial) => API.tests.questions.create(testsuite_id, question), { - onSuccess: () => { - toast.success("Test Question Added"); - TestQuestionsQuery.refetch(); - } + const router = useRouter(); + const { testsuite_id } = params; + + const testSuiteQuery = useQuery(["testsuite", testsuite_id], () => + API.tests.suites.get(testsuite_id) + ); + const testSuite: TestSuite | undefined = testSuiteQuery.data || undefined; + + const TestQuestionsQuery = useQuery(["testsuitequestion", testsuite_id], () => + API.tests.questions.list(testsuite_id, { ordering: "created_at" }) + ); + const testQuestions: TestQuestion[] | undefined = + TestQuestionsQuery.data?.results || undefined; + + const ProjectListQuery = useQuery(["projects"], () => API.projects.list()); + const projects: Project[] = ProjectListQuery.data?.results || []; + + type APIResponse = { + results: TestRun[]; + offset: number | null; + }; + + const fetchData: QueryFunction = async ({ pageParam }) => { + const offset = pageParam ? pageParam : 0; + const res = await API.tests.runs.list(testsuite_id, { + ordering: "-created_at", + offset: offset, + limit: 10, }); - - const TestQuestionDeleteMutation = useMutation((question_id: string) => API.tests.questions.delete(testsuite_id, question_id), { - onSuccess: () => { - toast.success("Test Question Deleted"); - TestQuestionsQuery.refetch(); - } - }); - - const TestRunCreateMutation = useMutation((testRun: Partial) => API.tests.runs.create(testsuite_id, testRun), { - onSuccess: (testRun) => { - toast.success("Test Started"); - refetch(); - } + const testRuns: TestRun[] = res.results; + return { + results: testRuns, + offset: offset + 10, + }; + }; + + const { data, fetchNextPage, hasNextPage, isFetching, isLoading, refetch } = + useInfiniteQuery({ + queryKey: ["testruns"], + queryFn: fetchData, + getNextPageParam: (lastPage, pages) => { + return lastPage.results.length > 0 ? lastPage.offset : null; + }, }); - const [currentQuestions, setCurrentQuestions] = useState([]); - const [currentQuestion, setCurrentQuestion] = useState>({}); - const [showAddQuestion, setShowAddQuestion] = useState(false); - const [saveBtnLoading, setSaveBtnLoading] = useState(false); - const [showRunTestSuite, setShowRunTestSuite] = useState(false); - const [testSuiteProject, setTestSuiteProject] = useState(); - - useEffect(() => { - if (testQuestions && testQuestions.length > 0) { - setCurrentQuestions(testQuestions.map((question) => { return { ...question } })); - } - }, [testQuestions]); - - useEffect(() => { - if (projects && projects.length > 0) { - setTestSuiteProject(projects[0].external_id); + useEffect(() => { + if ( + data?.pages + .flatMap((item) => item.results) + .find((item) => item.status === TestRunStatus.RUNNING) + ) { + setTimeout(() => { + refetch(); + }, 5000); + } + }, [data, refetch]); + + const testRuns = useMemo( + () => (data ? data?.pages.flatMap((item) => item.results) : []), + [data] + ); + + const observer = useRef(); + const lastElementRef = useCallback( + (node: HTMLButtonElement) => { + if (isLoading) return; + if (observer.current) observer.current.disconnect(); + observer.current = new IntersectionObserver((entries) => { + if (entries[0].isIntersecting && hasNextPage && !isFetching) { + fetchNextPage(); } - }, [projects]); - - const handleQuestionChange = (external_id: string | undefined, value: string): void => { - setCurrentQuestions(currentQuestions.map((question) => { - if (question.external_id === external_id) { - question.question = value; - } - return question; - })); + }); + if (node) observer.current.observe(node); + }, + [isLoading, hasNextPage, fetchNextPage, isFetching] + ); + + const TestQuestionsAddMutation = useMutation( + (question: Partial) => + API.tests.questions.create(testsuite_id, question), + { + onSuccess: () => { + toast.success("Test Question Added"); + TestQuestionsQuery.refetch(); + }, } - - const handleAnswerChange = (external_id: string | undefined, value: string): void => { - setCurrentQuestions(currentQuestions.map((question) => { - if (question.external_id === external_id) { - question.human_answer = value; - } - return question; - })); + ); + + const TestQuestionDeleteMutation = useMutation( + (question_id: string) => + API.tests.questions.delete(testsuite_id, question_id), + { + onSuccess: () => { + toast.success("Test Question Deleted"); + TestQuestionsQuery.refetch(); + }, } - - const handleLanguageChange = (external_id: string | undefined, value: string): void => { - setCurrentQuestions(currentQuestions.map((question) => { - if (question.external_id === external_id) { - question.language = value; - } - return question; - })); + ); + + const TestRunCreateMutation = useMutation( + (testRun: Partial) => API.tests.runs.create(testsuite_id, testRun), + { + onSuccess: (testRun) => { + toast.success("Test Started"); + refetch(); + }, } - - const handleAddQuestion = (question: string | undefined, human_answer: string | undefined, language: string | undefined): void => { - TestQuestionsAddMutation.mutate({ question, human_answer, language }); - setCurrentQuestion({}); - setShowAddQuestion(false); + ); + + const [currentQuestions, setCurrentQuestions] = useState([]); + const [currentQuestion, setCurrentQuestion] = useState>( + {} + ); + const [showAddQuestion, setShowAddQuestion] = useState(false); + const [saveBtnLoading, setSaveBtnLoading] = useState(false); + const [showRunTestSuite, setShowRunTestSuite] = useState(false); + const [testSuiteProject, setTestSuiteProject] = useState< + string | undefined + >(); + const [showDeleteModal, setShowDeleteModal] = useState(false); + + useEffect(() => { + if (testQuestions && testQuestions.length > 0) { + setCurrentQuestions( + testQuestions.map((question) => { + return { ...question }; + }) + ); } + }, [testQuestions]); - const handleQuestionDelete = (index: number): void => { - TestQuestionDeleteMutation.mutate(currentQuestions[index].external_id || ""); + useEffect(() => { + if (projects && projects.length > 0) { + setTestSuiteProject(projects[0].external_id); } - - const handleSave = (silent = false): void => { - if (!testQuestions) return; - setSaveBtnLoading(true); - - const modifiedQuestions: TestQuestion[] = []; - for (let i = 0; i < testQuestions.length; i++) { - const question = testQuestions[i]; - const currentQuestion = currentQuestions[i]; - if (question.question !== currentQuestion.question || question.human_answer !== currentQuestion.human_answer || question.language !== currentQuestion.language) { - modifiedQuestions.push({ ...currentQuestion }); - } + }, [projects]); + + const handleQuestionChange = ( + external_id: string | undefined, + value: string + ): void => { + setCurrentQuestions( + currentQuestions.map((question) => { + if (question.external_id === external_id) { + question.question = value; } - - const promises = modifiedQuestions.map((question) => { - if (question.external_id) return API.tests.questions.update(testsuite_id, question.external_id, question); - return Promise.resolve(); - }); - - Promise.all(promises).then(() => { - setSaveBtnLoading(false); - if (!silent) toast.success("Test Questions Saved"); - TestQuestionsQuery.refetch(); - }); + return question; + }) + ); + }; + + const handleAnswerChange = ( + external_id: string | undefined, + value: string + ): void => { + setCurrentQuestions( + currentQuestions.map((question) => { + if (question.external_id === external_id) { + question.human_answer = value; + } + return question; + }) + ); + }; + + const handleLanguageChange = ( + external_id: string | undefined, + value: string + ): void => { + setCurrentQuestions( + currentQuestions.map((question) => { + if (question.external_id === external_id) { + question.language = value; + } + return question; + }) + ); + }; + + const handleAddQuestion = ( + question: string | undefined, + human_answer: string | undefined, + language: string | undefined + ): void => { + TestQuestionsAddMutation.mutate({ question, human_answer, language }); + setCurrentQuestion({}); + setShowAddQuestion(false); + }; + + const handleQuestionDelete = (index: number): void => { + TestQuestionDeleteMutation.mutate( + currentQuestions[index].external_id || "" + ); + }; + + const handleSave = (silent = false): void => { + if (!testQuestions) return; + setSaveBtnLoading(true); + + const modifiedQuestions: TestQuestion[] = []; + for (let i = 0; i < testQuestions.length; i++) { + const question = testQuestions[i]; + const currentQuestion = currentQuestions[i]; + if ( + question.question !== currentQuestion.question || + question.human_answer !== currentQuestion.human_answer || + question.language !== currentQuestion.language + ) { + modifiedQuestions.push({ ...currentQuestion }); + } } - const startTestSuite = () => { - TestRunCreateMutation.mutate({ project: (testSuiteProject as any) }); - setShowRunTestSuite(false); - } + const promises = modifiedQuestions.map((question) => { + if (question.external_id) + return API.tests.questions.update( + testsuite_id, + question.external_id, + question + ); + return Promise.resolve(); + }); - const getCompletionStatus = (testRun: TestRun) => { - if (testRun.status === TestRunStatus.RUNNING) { - const totalQuestions = testQuestions?.length; - const answeredQuestions = testRun.test_results?.length; - const completedPercentage = ((answeredQuestions ?? 0) / (totalQuestions ?? 1)) * 100; - return `(${Math.round(Math.max(0, completedPercentage))}%)`; - } return "" + Promise.all(promises).then(() => { + setSaveBtnLoading(false); + if (!silent) toast.success("Test Questions Saved"); + TestQuestionsQuery.refetch(); + }); + }; + + const startTestSuite = () => { + TestRunCreateMutation.mutate({ project: testSuiteProject as any }); + setShowRunTestSuite(false); + }; + + const getCompletionStatus = (testRun: TestRun) => { + if (testRun.status === TestRunStatus.RUNNING) { + const totalQuestions = testQuestions?.length; + const answeredQuestions = testRun.test_results?.length; + const completedPercentage = + ((answeredQuestions ?? 0) / (totalQuestions ?? 1)) * 100; + return `(${Math.round(Math.max(0, completedPercentage))}%)`; } - - function formatDate(date: Date): string { - return `${date.getDate().toString().padStart(2, '0')}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getFullYear()} at ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`; + return ""; + }; + + function formatDate(date: Date): string { + return `${date.getDate().toString().padStart(2, "0")}-${( + date.getMonth() + 1 + ) + .toString() + .padStart(2, "0")}-${date.getFullYear()} at ${date + .getHours() + .toString() + .padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`; + } + + function getStatusClassName(status: number): string { + switch (status) { + case TestRunStatus.COMPLETED: + return "text-green-500"; + case TestRunStatus.FAILED: + return "text-red-500"; + case TestRunStatus.CANCELED: + return "text-orange-500"; + case TestRunStatus.RUNNING: + return "text-blue-400"; + default: + return "text-gray-500"; } - - function getStatusClassName(status: number): string { - switch (status) { - case TestRunStatus.COMPLETED: - return "text-green-500"; - case TestRunStatus.FAILED: - return "text-red-500"; - case TestRunStatus.CANCELED: - return "text-orange-500"; - case TestRunStatus.RUNNING: - return "text-blue-400"; - default: - return "text-gray-500"; - } + } + + const iconClassName = (status: number) => { + switch (status) { + case TestRunStatus.COMPLETED: + return "fa-regular fa-circle-check text-green-500"; + case TestRunStatus.FAILED: + return "fa-solid fa-circle-exclamation text-red-500"; + case TestRunStatus.CANCELED: + return "fa-solid fa-triangle-exclamation text-orange-500"; + default: + return "fa-solid fa-circle-stop text-red-500 hover:text-red-700 animate-pulse hover:animate-none"; } + }; - const iconClassName = (status: number) => { - switch (status) { - case TestRunStatus.COMPLETED: - return "fa-regular fa-circle-check text-green-500"; - case TestRunStatus.FAILED: - return "fa-solid fa-circle-exclamation text-red-500"; - case TestRunStatus.CANCELED: - return "fa-solid fa-triangle-exclamation text-orange-500"; - default: - return "fa-solid fa-circle-stop text-red-500 hover:text-red-700 animate-pulse hover:animate-none"; - } - }; - - - return ( -
-
-

Questions for {testSuite?.name}

-
- - - -
+ const deleteTest = () => { + API.tests.suites.delete(testsuite_id).then(() => { + toast.success("Test Suite Deleted"); + router.push("/admin"); + }); + }; + + return ( +
+
+

+ Questions for {testSuite?.name} +

+
+ + + +
+
+
+ {!currentQuestions || + (currentQuestions.length === 0 && ( +
+

No questions yet.

-
- {!currentQuestions || currentQuestions.length === 0 && ( -
-

No questions yet.

-
- ) - } - {currentQuestions && currentQuestions.length > 0 && ( -
- {currentQuestions.map((question, index) => ( -
-
- - -
-
-