├── .env
├── public
├── java.png
├── python.png
├── javascript.png
└── screenshot-for-readme.png
├── src
├── app
│ ├── favicon.ico
│ ├── fonts
│ │ ├── GeistVF.woff
│ │ └── GeistMonoVF.woff
│ ├── (root)
│ │ ├── layout.tsx
│ │ ├── schedule
│ │ │ ├── page.tsx
│ │ │ └── InterviewScheduleUI.tsx
│ │ ├── meeting
│ │ │ └── [id]
│ │ │ │ └── page.tsx
│ │ ├── recordings
│ │ │ └── page.tsx
│ │ └── (home)
│ │ │ └── page.tsx
│ ├── layout.tsx
│ ├── globals.css
│ └── (admin)
│ │ └── dashboard
│ │ └── page.tsx
├── components
│ ├── LoaderUI.tsx
│ ├── providers
│ │ ├── ThemeProvider.tsx
│ │ ├── ConvexClerkProvider.tsx
│ │ └── StreamClientProvider.tsx
│ ├── DasboardBtn.tsx
│ ├── UserInfo.tsx
│ ├── ui
│ │ ├── textarea.tsx
│ │ ├── label.tsx
│ │ ├── input.tsx
│ │ ├── switch.tsx
│ │ ├── badge.tsx
│ │ ├── avatar.tsx
│ │ ├── scroll-area.tsx
│ │ ├── resizable.tsx
│ │ ├── button.tsx
│ │ ├── card.tsx
│ │ ├── calendar.tsx
│ │ ├── dialog.tsx
│ │ ├── select.tsx
│ │ └── dropdown-menu.tsx
│ ├── Navbar.tsx
│ ├── ModeToggle.tsx
│ ├── EndCallButton.tsx
│ ├── ActionCard.tsx
│ ├── MeetingModal.tsx
│ ├── MeetingCard.tsx
│ ├── RecordingCard.tsx
│ ├── MeetingRoom.tsx
│ ├── MeetingSetup.tsx
│ ├── CommentDialog.tsx
│ └── CodeEditor.tsx
├── actions
│ └── stream.actions.ts
├── hooks
│ ├── useUserRole.ts
│ ├── useGetCallById.ts
│ ├── useMeetingActions.ts
│ └── useGetCalls.ts
├── middleware.ts
├── lib
│ └── utils.ts
└── constants
│ └── index.ts
├── next.config.mjs
├── postcss.config.mjs
├── convex
├── auth.config.ts
├── _generated
│ ├── api.js
│ ├── api.d.ts
│ ├── dataModel.d.ts
│ ├── server.js
│ └── server.d.ts
├── tsconfig.json
├── comments.ts
├── schema.ts
├── users.ts
├── interviews.ts
├── http.ts
└── README.md
├── .vscode
└── tasks.json
├── components.json
├── .gitignore
├── tsconfig.json
├── LICENSE
├── package.json
├── tailwind.config.ts
└── README.md
/.env:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/java.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/its-pratyushpandey/Codivue/HEAD/public/java.png
--------------------------------------------------------------------------------
/public/python.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/its-pratyushpandey/Codivue/HEAD/public/python.png
--------------------------------------------------------------------------------
/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/its-pratyushpandey/Codivue/HEAD/src/app/favicon.ico
--------------------------------------------------------------------------------
/public/javascript.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/its-pratyushpandey/Codivue/HEAD/public/javascript.png
--------------------------------------------------------------------------------
/src/app/fonts/GeistVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/its-pratyushpandey/Codivue/HEAD/src/app/fonts/GeistVF.woff
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/src/app/fonts/GeistMonoVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/its-pratyushpandey/Codivue/HEAD/src/app/fonts/GeistMonoVF.woff
--------------------------------------------------------------------------------
/public/screenshot-for-readme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/its-pratyushpandey/Codivue/HEAD/public/screenshot-for-readme.png
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/convex/auth.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | providers: [
3 | {
4 | domain: "https://climbing-cattle-3.clerk.accounts.dev/",
5 | applicationID: "convex",
6 | },
7 | ],
8 | };
9 |
--------------------------------------------------------------------------------
/src/app/(root)/layout.tsx:
--------------------------------------------------------------------------------
1 | import StreamClientProvider from "@/components/providers/StreamClientProvider";
2 |
3 | function Layout({ children }: { children: React.ReactNode }) {
4 | return
Meeting not found
25 |{action.description}
36 |39 | {recordings.length} {recordings.length === 1 ? "recording" : "recordings"} available 40 |
41 | 42 | {/* RECORDINGS GRID */} 43 | 44 |No recordings available
54 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 | A modern interview platform tailored for seamless video calls, real-time screen sharing, and secure candidate assessments. 15 |
16 | 17 | 18 | --- 19 | 20 | ## 🚀 Features 21 | 22 | - **Next.js & TypeScript** — Cutting-edge frontend stack for speed & reliability. 23 | - **Stream Integration** — Real-time video calls, screen sharing, and recording. 24 | - **Authentication & Authorization** — Secure access via Clerk. 25 | - **Convex** — Serverless backend for scalable data storage. 26 | - **Dynamic & Static Routing** — Flexible navigation architecture. 27 | - **Server & Client Components** — Optimized data fetching and UI updates. 28 | - **Server Actions** — Secure backend operations with Next.js. 29 | - **Modern UI** — Styled with Tailwind CSS and Shadcn for a professional look. 30 | - **Responsive Design** — Works great on desktops, laptops & mobile devices. 31 | 32 | --- 33 | 34 | ## 🛠️ Getting Started 35 | 36 | ### 1. Clone the Repository 37 | 38 | ```shell 39 | git clone https://github.com/your-username/video-calling-interview-platform.git 40 | cd video-calling-interview-platform 41 | ``` 42 | 43 | ### 2. Install Dependencies 44 | 45 | ```shell 46 | npm install 47 | ``` 48 | 49 | ### 3. Configure Environment Variables 50 | 51 | Create a `.env.local` file at the root of your project and add the following variables: 52 | 53 | ```env 54 | NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_clerk_publishable_key 55 | CLERK_SECRET_KEY=your_clerk_secret_key 56 | CONVEX_DEPLOYMENT=your_convex_deployment_url 57 | NEXT_PUBLIC_CONVEX_URL=your_convex_url 58 | NEXT_PUBLIC_STREAM_API_KEY=your_stream_api_key 59 | STREAM_SECRET_KEY=your_stream_secret_key 60 | ``` 61 | 62 | > 💡 **Tip:** Never commit your `.env.local` file. Store secrets securely. 63 | 64 | ### 4. Start the Development Server 65 | 66 | ```shell 67 | npm run dev 68 | ``` 69 | 70 | Visit [http://localhost:3000](http://localhost:3000) to explore the platform. 71 | 72 | --- 73 | 74 | ## 📱 Responsive & Attractive Design 75 | 76 | - **Mobile First:** Optimized layouts for all devices. 77 | - **Intuitive UI:** Easy navigation and user-friendly controls. 78 | - **Professional Styling:** Clean, modern look with Tailwind and Shadcn components. 79 | 80 | --- 81 | 82 | ## 📚 Documentation 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx"; 2 | import { addHours, intervalToDuration, isAfter, isBefore, isWithinInterval } from "date-fns"; 3 | import { twMerge } from "tailwind-merge"; 4 | import { Doc } from "../../convex/_generated/dataModel"; 5 | 6 | export function cn(...inputs: ClassValue[]) { 7 | return twMerge(clsx(inputs)); 8 | } 9 | type Interview = Doc<"interviews">; 10 | type User = Doc<"users">; 11 | 12 | export const groupInterviews = (interviews: Interview[]) => { 13 | if (!interviews) return {}; 14 | 15 | return interviews.reduce((acc: any, interview: Interview) => { 16 | const date = new Date(interview.startTime); 17 | const now = new Date(); 18 | 19 | if (interview.status === "succeeded") { 20 | acc.succeeded = [...(acc.succeeded || []), interview]; 21 | } else if (interview.status === "failed") { 22 | acc.failed = [...(acc.failed || []), interview]; 23 | } else if (isBefore(date, now)) { 24 | acc.completed = [...(acc.completed || []), interview]; 25 | } else if (isAfter(date, now)) { 26 | acc.upcoming = [...(acc.upcoming || []), interview]; 27 | } 28 | 29 | return acc; 30 | }, {}); 31 | }; 32 | 33 | export const getCandidateInfo = (users: User[], candidateId: string) => { 34 | const candidate = users?.find((user) => user.clerkId === candidateId); 35 | return { 36 | name: candidate?.name || "Unknown Candidate", 37 | image: candidate?.image || "", 38 | initials: 39 | candidate?.name 40 | ?.split(" ") 41 | .map((n) => n[0]) 42 | .join("") || "UC", 43 | }; 44 | }; 45 | 46 | export const getInterviewerInfo = (users: User[], interviewerId: string) => { 47 | const interviewer = users?.find((user) => user.clerkId === interviewerId); 48 | return { 49 | name: interviewer?.name || "Unknown Interviewer", 50 | image: interviewer?.image, 51 | initials: 52 | interviewer?.name 53 | ?.split(" ") 54 | .map((n) => n[0]) 55 | .join("") || "UI", 56 | }; 57 | }; 58 | 59 | export const calculateRecordingDuration = (startTime: string, endTime: string) => { 60 | const start = new Date(startTime); 61 | const end = new Date(endTime); 62 | 63 | const duration = intervalToDuration({ start, end }); 64 | 65 | if (duration.hours && duration.hours > 0) { 66 | return `${duration.hours}:${String(duration.minutes).padStart(2, "0")}:${String( 67 | duration.seconds 68 | ).padStart(2, "0")}`; 69 | } 70 | 71 | if (duration.minutes && duration.minutes > 0) { 72 | return `${duration.minutes}:${String(duration.seconds).padStart(2, "0")}`; 73 | } 74 | 75 | return `${duration.seconds} seconds`; 76 | }; 77 | 78 | export const getMeetingStatus = (interview: Interview) => { 79 | const now = new Date(); 80 | const interviewStartTime = interview.startTime; 81 | const endTime = addHours(interviewStartTime, 1); 82 | 83 | if ( 84 | interview.status === "completed" || 85 | interview.status === "failed" || 86 | interview.status === "succeeded" 87 | ) 88 | return "completed"; 89 | if (isWithinInterval(now, { start: interviewStartTime, end: endTime })) return "live"; 90 | if (isBefore(now, interviewStartTime)) return "upcoming"; 91 | return "completed"; 92 | }; 93 | -------------------------------------------------------------------------------- /src/components/ui/calendar.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import { ChevronLeft, ChevronRight } from "lucide-react" 5 | import { DayPicker } from "react-day-picker" 6 | 7 | import { cn } from "@/lib/utils" 8 | import { buttonVariants } from "@/components/ui/button" 9 | 10 | export type CalendarProps = React.ComponentProps48 | {isInterviewer 49 | ? "Manage your interviews and review candidates effectively" 50 | : "Access your upcoming interviews and preparations"} 51 |
52 |View and join your scheduled interviews
78 |Make sure you look good!
38 |{call.id}
56 |Camera
68 |69 | {isCameraDisabled ? "Off" : "On"} 70 |
71 |Microphone
87 |88 | {isMicDisabled ? "Off" : "On"} 89 |
90 |Settings
106 |Configure devices
107 |119 | Do not worry, our team is super friendly! We want you to succeed. 🎉 120 |
121 |{interview.title}
75 |39 | Choose your language and solve the problem 40 |
41 |{selectedQuestion.description}
99 |Example {index + 1}:
115 |117 |125 |Input: {example.input}118 |Output: {example.output}119 | {example.explanation && ( 120 |121 | Explanation: {example.explanation} 122 |123 | )} 124 |
Schedule and manage interviews
141 |