Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions The-Preppal
Submodule The-Preppal added at 1c5344
40 changes: 40 additions & 0 deletions preppal/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# env files (can opt-in for commiting if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
57 changes: 57 additions & 0 deletions preppal/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# PrepPal

**PrepPal** is an AI-driven web application designed to help users prepare for interviews effectively by providing feedback on their responses. It features question prompts, feedback on answers, and suggestions for improvement, making it a comprehensive tool for interview preparation.

## Table of Contents

- [Features](#features)
- [Technologies Used](#technologies-used)
- [Getting Started](#getting-started)
- [Usage](#usage)
- [Project Structure](#project-structure)
---

### Features

- **Question Prompts:** Generate interview questions based on topics or difficulty level.
- **Real-time Feedback:** Get instant feedback on your responses, including suggestions for improvement.
- **Collapsible Feedback View:** User-friendly interface with collapsible sections for feedback per question.
- **Session Storage:** Each interview session is stored, allowing users to review past feedback and track improvement.

### Technologies Used

- **Frontend:** React, Next.js, Tailwind CSS
- **Backend:** Node.js, Drizzle ORM, PostgreSql
- **Other:** Lucide React Icons for UI icons, Expo for mobile compatibility (if applicable)

---

### Getting Started

#### Prerequisites

- Node.js (version 16 or 18 is recommended)
- npm or yarn (package manager)


### Usage

1. *Create an Interview Session:** Start a new interview by selecting a topic or difficulty.
2. **Answer Questions:** Respond to questions, and receive real-time feedback.
3. **Review Feedback:** Access detailed feedback on each answer, including suggestions for improvement.
4. **Start a New Session:** Return to the dashboard and start another session.

---

### Project Structure

```bash
.
├── components # Reusable UI components (buttons, collapsibles)
├── pages # Next.js pages (dashboard, feedback, etc.)
├── utils # Utility functions (db connection, schema, etc.)
├── public # Static assets
├── styles # Global styles, Tailwind CSS configuration
├── .env.local # Environment variables
└── README.md # Project documentation
```
17 changes: 17 additions & 0 deletions preppal/app/(auth)/sign-in/[[...sign-in]]/page.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { SignIn } from '@clerk/nextjs';

export default function Page() {
return (
<div className="flex justify-center items-center h-full my-20">
{/* Background for Light Mode and Dark Mode */}
<div
className="absolute inset-0 -z-10 h-full w-full px-5 py-24
[background:radial-gradient(125%_125%_at_50%_10%,#e0f2fe_40%,#3b82f6_100%)]
dark:[background:radial-gradient(125%_125%_at_50%_10%,#000_40%,#3b82f6_100%)]"
></div>

{/* SignIn Component */}
<SignIn />
</div>
);
}
9 changes: 9 additions & 0 deletions preppal/app/(auth)/sign-up/[[...sign-up]]/page.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { SignUp } from '@clerk/nextjs';

export default function Page() {
return (
<div className='flex justify-center items-center h-screen'>
<SignUp />
</div>
);
}
88 changes: 88 additions & 0 deletions preppal/app/aboutme/page.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"use client";
import React, { useState, useEffect } from "react";
import { FaGithub, FaInstagram, FaLinkedin } from "react-icons/fa";
import Spline from "@splinetool/react-spline";

function AboutMe() {
const [isLoading, setIsLoading] = useState(true);
const [progress, setProgress] = useState(0);

useEffect(() => {
let interval;
if (isLoading) {
interval = setInterval(() => {
setProgress((prev) => (prev < 99 ? prev + 1 : prev));
}, 50);
}
return () => clearInterval(interval);
}, [isLoading]);

const handleLoad = () => {
setIsLoading(false);
setProgress(100);
};

return (
<>
<div className="relative h-screen overflow-y-hidden">
{/* Message and Name */}
<div className="absolute top-4 right-4 z-10 text-xl font-serif text-right sm:text-xl md:text-center lg:text-left">
<p className="sm:text-base sm:mt-1 md:text-lg">
"All I can say is, best of luck with your placements, and have an amazing day ahead!
</p>
<a
href="mailto:yeddulamadhu6@gmail.com"
aria-label="Send an email to Y.Madhu"
className="mt-1 ml-72 text-center sm:text-left font-serif hover:cursor-pointer hover:text-blue-200"
>
~Y.M.M.R~
</a>

{/* Icons just below the name */}
<div className="mt-3 ml-3 flex justify-center space-x-6">
<a
href="https://www.linkedin.com/in/madhu-yeddula/"
target="_blank"
rel="noopener noreferrer"
aria-label="Visit Madhu's LinkedIn profile"
>
<FaLinkedin className="text-2xl text-black dark:text-white sm:text-xl hover:text-blue-700 transition" />
</a>
<a
href="https://github.com/ymadhumohanreddy"
target="_blank"
rel="noopener noreferrer"
aria-label="Visit Madhu's GitHub profile"
>
<FaGithub className="text-2xl text-black dark:text-white sm:text-xl hover:text-gray-600 transition" />
</a>
<a
href="https://www.instagram.com/madhu_mohanreddy/"
target="_blank"
rel="noopener noreferrer"
aria-label="Visit Madhu's Instagram profile"
>
<FaInstagram className="text-2xl text-black dark:text-white sm:text-xl hover:text-pink-500 transition" />
</a>
</div>
</div>

{/* Loading Spinner */}
{isLoading && (
<div className="absolute z-10 flex flex-col items-center justify-center w-full h-full bg-white">
<div className="loader border-4 border-blue-400 border-t-transparent rounded-full w-12 h-12 animate-spin"></div>
<p className="mt-4 text-lg font-medium text-blue-500">
Loading {progress}%
</p>
</div>
)}

{/* Spline Component */}
<Spline scene="https://prod.spline.design/sTUJrn9hWcezm1uG/scene.splinecode" onLoad={handleLoad} />
</div>
</>
);
}

export default AboutMe;

152 changes: 152 additions & 0 deletions preppal/app/dashboard/_components/AddNewInterview.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
"use client";
import React, { useState } from 'react';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "../../../components/ui/dialog";
import { Button } from '../../../components/ui/button';
import { Input } from '../../../components/ui/input';
import { Textarea } from '../../../components/ui/textarea';
import { chatSession } from '../../../utils/GeminiAIModal';
import { db } from '../../../utils/db';
import { MockInterview } from '../../../utils/schema';
import { LoaderCircle } from 'lucide-react';
import { v4 as uuidv4 } from 'uuid';
import { useUser } from '@clerk/nextjs';
import moment from 'moment';
import { useRouter } from 'next/navigation';

function AddNewInterview() {
const [openDialog, setOpenDialog] = useState(false);
const [jobPosition, setJobPosition] = useState('');
const [jobDesc, setJobDesc] = useState('');
const [jobExperience, setJobExperience] = useState('');
const [loading, setLoading] = useState(false);
const [jsonResponse, setJsonResponse] = useState([]);
const router = useRouter();
const { user } = useUser();

const onSubmit = async (e) => {
setLoading(true);
e.preventDefault();
console.log(jobPosition, jobDesc, jobExperience);

const InputPrompt = `Job position:${jobPosition},Job Description:${jobDesc},Years of Experience:${jobExperience},Depends on Job Position,Job Description & Years of Experience give me ${process.env.NEXT_PUBLIC_INTERVIEW_QUESTION_COUNT} interview questions along with Answer in json format,give us question and answer field in json`;

try {
const result = await chatSession.sendMessage(InputPrompt);
let MockJsonResp = result.response.text();

// Log the raw response to check its content
console.log("Raw Response:", MockJsonResp);

// Clean up response to ensure it's JSON
MockJsonResp = MockJsonResp.replace(/```json/g, "")
.replace(/```/g, "")
.replace(/\n/g, "")
.trim();

// Attempt to parse JSON response
const parsedResponse = JSON.parse(MockJsonResp);
setJsonResponse(parsedResponse);

if (parsedResponse) {
const resp = await db.insert(MockInterview).values({
mockId: uuidv4(),
jsonMockResp: MockJsonResp,
jobPosition: jobPosition,
jobDesc: jobDesc,
jobExperience: jobExperience,
createdBy: user?.primaryEmailAddress?.emailAddress,
createdAt: moment().format('DD-MM-yyyy'),
}).returning({ mockId: MockInterview.mockId });

console.log("Inserted ID:", resp);
if (resp) {
setOpenDialog(false);
router.push('/dashboard/interview/' + resp[0]?.mockId);
}
} else {
console.log("Error: No valid JSON response received.");
}
} catch (error) {
console.error("Error occurred while processing:", error);
} finally {
setLoading(false);
}
};

return (
<div>
<div
className='p-16 border rounded-lg bg-secondary hover:scale-105 hover:shadow-md cursor-pointer transition-all'
onClick={() => setOpenDialog(true)}
>
<h2 className='text-lg text-center'>+ Add New</h2>
</div>

<Dialog open={openDialog} onOpenChange={setOpenDialog}>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle className='font-serif text-xl text-blue-400'>Please share your interview details</DialogTitle>
<div className="font-serif text-sm text-muted-foreground">
<div className="mt-2 mb-6">
Please add details about the job position/role, job description, and years of experience.
</div>
<form onSubmit={onSubmit}>
<div className='mt-7 my-3 text-blue-400'>
<label htmlFor="jobPosition">Job Role/Job Position</label>
<Input
id="jobPosition"
placeholder="Ex. SDE-1"
required
className="text-black dark:text-white" // Apply text-black for input text color
onChange={(e) => setJobPosition(e.target.value)}
/>
</div>
<div className='my-3 text-blue-400'>
<label htmlFor="jobDesc">Job Description / Tech Stack</label>
<Textarea
id="jobDesc"
placeholder="Ex. DSA, React, Behavioral questions, etc."
required
className="text-black dark:text-white" // Apply text-black for textarea text color
onChange={(e) => setJobDesc(e.target.value)}
/>
</div>
<div className='my-3 text-blue-400'>
<label htmlFor="jobExperience">Years of Experience</label>
<Input
id="jobExperience"
placeholder="Ex. 10"
type="number"
max="100"
required
className="text-black dark:text-white" // Apply text-black for input text color
onChange={(e) => setJobExperience(e.target.value)}
/>
</div>
<div className='flex gap-5 justify-end mt-6'>
<Button type="button" variant="ghost" onClick={() => setOpenDialog(false)}>
Cancel
</Button>
<Button className="bg-blue-400 hover:bg-blue-700" type="submit" disabled={loading}>
{loading ? (
<>
<LoaderCircle className='animate-spin' /> Generating from AI
</>
) : 'Start Interview'}
</Button>
</div>
</form>
</div>
</DialogHeader>
</DialogContent>
</Dialog>
</div>
);
}

export default AddNewInterview;
Loading
Loading