Advertisement
windrunner

GenerateTest.tsx

Apr 7th, 2025
378
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 10.85 KB | Source Code | 0 0
  1. import React, { useState, useEffect } from 'react';
  2. import { useParams } from 'react-router-dom';
  3. import axios from 'axios';
  4. import { Header } from './Header';
  5. import { Footer } from './Footer';
  6. import { Button } from "@/components/ui/button";
  7. import { Loader2, TestTube, BadgeCheck, BookOpen, Download } from "lucide-react";
  8. import { cn } from "@/lib/utils";
  9. import { useTheme } from "@/hooks/useTheme";
  10. import { useScrollAnimation } from "@/hooks/useScrollAnimation";
  11. import '../styles/GenerateTest.css';
  12. import '../styles/MockTest.css';
  13. import { BlockMath, InlineMath } from "react-katex";
  14. import "katex/dist/katex.min.css";
  15.  
  16. // Subject background images and colors
  17. const subjectThemes: Record<string, { bgImage: string, primaryColor: string, icon: React.ElementType }> = {
  18.   biology: {
  19.     bgImage: "https://images.unsplash.com/photo-1530026405186-ed1f139313f8",
  20.     primaryColor: "rgba(16, 185, 129, 0.8)", // green
  21.     icon: TestTube
  22.   },
  23.   chemistry: {
  24.     bgImage: "https://images.unsplash.com/photo-1603126857599-f6e157fa2fe6",
  25.     primaryColor: "rgba(124, 58, 237, 0.8)", // purple
  26.     icon: TestTube
  27.   },
  28.   physics: {
  29.     bgImage: "https://images.unsplash.com/photo-1636466497217-26a8cbeaf0aa",
  30.     primaryColor: "rgba(59, 130, 246, 0.8)", // blue
  31.     icon: TestTube
  32.   },
  33.   mathematics: {
  34.     bgImage: "https://images.unsplash.com/photo-1635372722656-389f87a941db",
  35.     primaryColor: "rgba(239, 68, 68, 0.8)", // red
  36.     icon: TestTube
  37.   },
  38.   default: {
  39.     bgImage: "https://images.unsplash.com/photo-1488190211105-8b0e65b80b4e",
  40.     primaryColor: "rgba(79, 70, 229, 0.8)", // indigo
  41.     icon: TestTube
  42.   }
  43. };
  44.  
  45. const GenerateTest = () => {
  46.   const { subject = "" } = useParams();
  47.   const [loading, setLoading] = useState(false);
  48.   const [testResult, setTestResult] = useState('');
  49.   const { isDarkMode } = useTheme();
  50.  
  51.   // Animation refs
  52.   const fadeInRef = useScrollAnimation({ animation: 'fade' });
  53.   const slideUpRef = useScrollAnimation({ animation: 'slide', direction: 'up', delay: 3 });
  54.  
  55.   // Format subject for display (capitalize first letter)
  56.   const formattedSubject = subject
  57.     .replace(/_/g, " ")
  58.     .split(" ")
  59.     .map(word => word.charAt(0).toUpperCase() + word.slice(1))
  60.     .join(" ");
  61.  
  62.   const subjectKey = subject.toLowerCase().replace(/_/g, "");
  63.   const { bgImage, primaryColor, icon: SubjectIcon } = subjectThemes[subjectKey] || subjectThemes.default;
  64.  
  65.   const generateTest = async () => {
  66.     setLoading(true);
  67.     setTestResult('');
  68.    
  69.     try {
  70.       const res = await axios.post("http://127.0.0.1:8001/api/generate", {
  71.         subject: subject.toLowerCase(),
  72.       });
  73.       setTestResult(res.data.mock_test);
  74.     } catch (error: any) {
  75.       console.error("Error generating test:", error.response?.data || error);
  76.       alert("An error occurred while generating the test.");
  77.     } finally {
  78.       setLoading(false);
  79.     }
  80.   };
  81.  
  82.   const handlePrint = () => {
  83.     window.print();
  84.   };
  85.  
  86.   const formatResponse = (text: string): JSX.Element[] | null => {
  87.     if (!text) return null;
  88.  
  89.     const formattedElements: JSX.Element[] = [];
  90.     let currentOptions: JSX.Element[] = [];
  91.     let questionCount = 0;
  92.     let insideSection = false;
  93.  
  94.     text.split("\n").forEach((line, index) => {
  95.       if (line.startsWith("###")) {
  96.         questionCount = 0;
  97.         insideSection = true;
  98.         formattedElements.push(
  99.           <h3 key={index} className="formatted-subheading">
  100.             {line.replace("###", "").trim()}
  101.           </h3>
  102.         );
  103.       } else if (line.match(/^\d+\./)) {
  104.         if (insideSection) {
  105.           questionCount++;
  106.           const questionText = line.replace(/^\d+\.\s*/, "").trim();
  107.           const containsMath = questionText.includes("$") || questionText.includes("\\(");
  108.  
  109.           formattedElements.push(
  110.             <div key={index} className="mcq-container">
  111.               <p className="mcq-question">
  112.                 {`${questionCount}. `}
  113.                 {containsMath ? <BlockMath>{questionText}</BlockMath> : questionText}
  114.               </p>
  115.             </div>
  116.           );
  117.         } else {
  118.           formattedElements.push(
  119.             <p key={index} className="formatted-text">
  120.               {line}
  121.             </p>
  122.           );
  123.         }
  124.       } else if (line.match(/^[a-d]\)/i)) {
  125.         const optionText = line.replace(/^[a-d]\)\s*/, "").trim();
  126.         const containsMath = optionText.includes("$") || optionText.includes("\\(");
  127.  
  128.         currentOptions.push(
  129.           <span key={index} className="mcq-option">
  130.             {containsMath ? <InlineMath>{optionText}</InlineMath> : optionText}
  131.           </span>
  132.         );
  133.       } else {
  134.         const containsMath = line.includes("$") || line.includes("\\(");
  135.  
  136.         formattedElements.push(
  137.           <p key={index} className="formatted-text">
  138.             {containsMath ? <InlineMath>{line}</InlineMath> : line}
  139.           </p>
  140.         );
  141.       }
  142.     });
  143.  
  144.     if (currentOptions.length > 0) {
  145.       formattedElements.push(
  146.         <div key="final-options" className="mcq-options">{currentOptions}</div>
  147.       );
  148.     }
  149.  
  150.     return formattedElements;
  151.   };
  152.  
  153.   return (
  154.     <div className={cn(
  155.       "min-h-screen",
  156.       isDarkMode
  157.         ? "bg-gradient-to-b from-gray-900 via-gray-800 to-gray-900"
  158.         : "bg-gradient-to-b from-blue-50 via-indigo-50/30 to-white"
  159.     )}>
  160.       <Header />
  161.      
  162.       {/* Particles background */}
  163.       <div id="particles-js" className="fixed inset-0 pointer-events-none"></div>
  164.      
  165.       <div className="pt-28 pb-20 px-4 container max-w-4xl mx-auto relative">
  166.         {/* Hero section with background image */}
  167.         <div className="text-center mb-10" ref={fadeInRef}>
  168.           <div className="inline-block p-2 rounded-full bg-primary/10 mb-4 animate-bounce">
  169.             <SubjectIcon className="h-6 w-6 text-primary" />
  170.           </div>
  171.          
  172.           <h1 className={cn(
  173.             "text-3xl md:text-5xl font-bold mb-4",
  174.             "animate-fade-in"
  175.           )}>
  176.             Generate {formattedSubject} Mock Test
  177.           </h1>
  178.          
  179.           <p className="text-muted-foreground text-lg max-w-2xl mx-auto mb-8 animate-fade-in stagger-1">
  180.             Create customized mock tests with detailed solutions to test your knowledge and prepare for exams.
  181.           </p>
  182.          
  183.           <div className="flex items-center justify-center gap-4 mb-12 animate-fade-in stagger-2">
  184.             <Button
  185.               size="lg"
  186.               onClick={generateTest}
  187.               className="gap-2 px-8 py-6 h-auto rounded-full font-semibold text-lg shadow-lg hover:shadow-xl hover:scale-105 transition-all"
  188.               style={{
  189.                 background: primaryColor,
  190.                 borderColor: 'transparent'
  191.               }}
  192.               disabled={loading}
  193.             >
  194.               {loading ? (
  195.                 <>
  196.                   <Loader2 className="h-5 w-5 animate-spin" />
  197.                   Generating Test...
  198.                 </>
  199.               ) : (
  200.                 <>
  201.                   <TestTube className="h-5 w-5" />
  202.                   Create {formattedSubject} Test
  203.                 </>
  204.               )}
  205.             </Button>
  206.           </div>
  207.          
  208.           <div className="grid grid-cols-1 md:grid-cols-3 gap-6 max-w-3xl mx-auto animate-fade-in stagger-3">
  209.             <div className={cn(
  210.               "rounded-xl p-5 flex flex-col items-center text-center hover:scale-105 transition-all",
  211.               isDarkMode ? "bg-gray-800/50" : "bg-white/80 shadow-sm",
  212.               "border border-primary/10"
  213.             )}>
  214.               <BookOpen className="h-10 w-10 text-primary mb-3" />
  215.               <h3 className="font-semibold text-lg mb-1">Curriculum-Aligned</h3>
  216.               <p className="text-muted-foreground text-sm">Tests based on the official syllabus and exam patterns</p>
  217.             </div>
  218.            
  219.             <div className={cn(
  220.               "rounded-xl p-5 flex flex-col items-center text-center hover:scale-105 transition-all",
  221.               isDarkMode ? "bg-gray-800/50" : "bg-white/80 shadow-sm",
  222.               "border border-primary/10"
  223.             )}>
  224.               <BadgeCheck className="h-10 w-10 text-primary mb-3" />
  225.               <h3 className="font-semibold text-lg mb-1">Detailed Solutions</h3>
  226.               <p className="text-muted-foreground text-sm">Step-by-step explanations for every question</p>
  227.             </div>
  228.            
  229.             <div className={cn(
  230.               "rounded-xl p-5 flex flex-col items-center text-center hover:scale-105 transition-all",
  231.               isDarkMode ? "bg-gray-800/50" : "bg-white/80 shadow-sm",
  232.               "border border-primary/10"
  233.             )}>
  234.               <Download className="h-10 w-10 text-primary mb-3" />
  235.               <h3 className="font-semibold text-lg mb-1">Downloadable PDF</h3>
  236.               <p className="text-muted-foreground text-sm">Save tests for offline practice and reference</p>
  237.             </div>
  238.           </div>
  239.         </div>
  240.  
  241.         {/* Test Result */}
  242.         {loading && (
  243.           <div className="flex flex-col items-center justify-center p-10 text-center">
  244.             <div className="w-16 h-16 border-4 border-primary/30 border-t-primary rounded-full animate-spin mb-4"></div>
  245.             <p className="text-lg font-medium">Generating your test...</p>
  246.             <p className="text-muted-foreground">This may take a few moments</p>
  247.           </div>
  248.         )}
  249.        
  250.         {testResult && (
  251.           <div
  252.             className={cn(
  253.               "rounded-2xl p-6 md:p-8 animate-fade-in",
  254.               isDarkMode ? "bg-gray-800/70" : "bg-white/90 shadow-lg",
  255.               "border border-primary/10"
  256.             )}
  257.             ref={slideUpRef}
  258.           >
  259.             <div className="mb-6 flex justify-between items-center">
  260.               <h2 className="text-2xl font-bold">{formattedSubject} Mock Test</h2>
  261.               <Button variant="outline" className="rounded-full gap-2" onClick={handlePrint}>
  262.                 <Download className="h-4 w-4" />
  263.                 Download PDF
  264.               </Button>
  265.             </div>
  266.            
  267.             <div className="test-result">
  268.               <div className="formatted-output">{formatResponse(testResult)}</div>
  269.             </div>
  270.            
  271.             <div className="mt-8 flex flex-col sm:flex-row gap-4 justify-center">
  272.               <Button
  273.                 onClick={generateTest}
  274.                 className="gap-2"
  275.                 variant="outline"
  276.               >
  277.                 <TestTube className="h-4 w-4" />
  278.                 Generate Another Test
  279.               </Button>
  280.              
  281.               <Button className="gap-2">
  282.                 View Solutions
  283.               </Button>
  284.             </div>
  285.           </div>
  286.         )}
  287.       </div>
  288.      
  289.       <Footer />
  290.     </div>
  291.   );
  292. };
  293.  
  294. export default GenerateTest;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement