├── app ├── favicon.ico ├── (auth) │ ├── sign-in │ │ └── [[...sign-in]] │ │ │ └── page.tsx │ ├── sign-up │ │ └── [[...sign-up]] │ │ │ └── page.tsx │ ├── layout.tsx │ └── onboarding │ │ └── page.tsx ├── (root) │ ├── tags │ │ ├── [id] │ │ │ ├── loading.tsx │ │ │ └── page.tsx │ │ ├── loading.tsx │ │ └── page.tsx │ ├── collection │ │ ├── loading.tsx │ │ └── page.tsx │ ├── community │ │ ├── loading.tsx │ │ └── page.tsx │ ├── layout.tsx │ ├── ask-question │ │ └── page.tsx │ ├── profile │ │ ├── edit │ │ │ └── page.tsx │ │ └── [id] │ │ │ └── loading.tsx │ ├── jobs │ │ ├── loading.tsx │ │ └── page.tsx │ ├── question │ │ └── edit │ │ │ └── [id] │ │ │ └── page.tsx │ ├── edit-answer │ │ └── [id] │ │ │ └── page.tsx │ └── (home) │ │ ├── loading.tsx │ │ └── page.tsx ├── api │ ├── rapidapi │ │ └── route.ts │ ├── openai │ │ └── route.ts │ └── webhook │ │ └── clerk │ │ └── route.ts └── layout.tsx ├── postcss.config.js ├── public ├── assets │ ├── images │ │ ├── logo.png │ │ ├── auth-dark.png │ │ ├── auth-light.png │ │ ├── dark-illustration.png │ │ ├── light-illustration.png │ │ ├── site-logo.svg │ │ └── default-logo.svg │ └── icons │ │ ├── arrow-right.svg │ │ ├── arrow-left.svg │ │ ├── star.svg │ │ ├── tag.svg │ │ ├── avatar.svg │ │ ├── computer.svg │ │ ├── moon.svg │ │ ├── briefcase.svg │ │ ├── arrow-up-right.svg │ │ ├── clock-2.svg │ │ ├── clock.svg │ │ ├── hamburger.svg │ │ ├── message.svg │ │ ├── search.svg │ │ ├── trash.svg │ │ ├── star-red.svg │ │ ├── star-filled.svg │ │ ├── downvote.svg │ │ ├── upvote.svg │ │ ├── currency-dollar-circle.svg │ │ ├── people.svg │ │ ├── edit.svg │ │ ├── downvoted.svg │ │ ├── upvoted.svg │ │ ├── job-search.svg │ │ ├── chevron-down.svg │ │ ├── mingcute-down-line.svg │ │ ├── link.svg │ │ ├── chevron-right.svg │ │ ├── location.svg │ │ ├── close.svg │ │ ├── suitcase.svg │ │ ├── account.svg │ │ ├── user.svg │ │ ├── eye.svg │ │ ├── stars.svg │ │ ├── carbon-location.svg │ │ ├── sun.svg │ │ ├── sign-up.svg │ │ ├── bronze-medal.svg │ │ ├── like.svg │ │ ├── home.svg │ │ ├── silver-medal.svg │ │ ├── question.svg │ │ ├── au.svg │ │ └── gold-medal.svg ├── vercel.svg └── next.svg ├── .eslintrc.json ├── .vscode └── settings.json ├── components.json ├── components ├── ui │ ├── skeleton.tsx │ ├── label.tsx │ ├── textarea.tsx │ ├── input.tsx │ ├── toaster.tsx │ ├── badge.tsx │ ├── switch.tsx │ ├── button.tsx │ └── tabs.tsx ├── shared │ ├── ProfileLink.tsx │ ├── RenderTag.tsx │ ├── AnswersTab.tsx │ ├── Metric.tsx │ ├── QuestionsTab.tsx │ ├── NoResult.tsx │ ├── Switcher.tsx │ ├── ParseHTML.tsx │ ├── navbar │ │ ├── Navbar.tsx │ │ ├── Theme.tsx │ │ └── Mobile.tsx │ ├── EditDeleteAction.tsx │ ├── Pagination.tsx │ ├── search │ │ ├── GlobalFilters.tsx │ │ ├── LocalSearchbar.tsx │ │ ├── GlobalSearch.tsx │ │ └── GlobalResult.tsx │ ├── RightSidebar.tsx │ ├── Filters.tsx │ ├── Stats.tsx │ ├── Filter.tsx │ ├── LeftSidebar.tsx │ └── AllAnswers.tsx ├── jobs │ └── JobBadge.tsx └── cards │ ├── UserCard.tsx │ ├── AnswerCard.tsx │ └── QuestionCard.tsx ├── next.config.js ├── .gitignore ├── .env.example ├── lib ├── mongoose.ts ├── validations.ts └── actions │ ├── interaction.action.ts │ ├── general.action.ts │ └── job.action.ts ├── tsconfig.json ├── database ├── tag.model.ts ├── answer.model.ts ├── interaction.model.ts ├── question.model.ts └── user.model.ts ├── middleware.ts ├── LICENSE ├── context └── ThemeProvider.tsx ├── types └── index.d.ts ├── constants ├── filters.ts └── index.ts ├── package.json ├── tailwind.config.ts └── styles └── prism.css /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladunjexa/nextjs14-devoverflow/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladunjexa/nextjs14-devoverflow/HEAD/public/assets/images/logo.png -------------------------------------------------------------------------------- /public/assets/images/auth-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladunjexa/nextjs14-devoverflow/HEAD/public/assets/images/auth-dark.png -------------------------------------------------------------------------------- /public/assets/images/auth-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladunjexa/nextjs14-devoverflow/HEAD/public/assets/images/auth-light.png -------------------------------------------------------------------------------- /public/assets/images/dark-illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladunjexa/nextjs14-devoverflow/HEAD/public/assets/images/dark-illustration.png -------------------------------------------------------------------------------- /public/assets/images/light-illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ladunjexa/nextjs14-devoverflow/HEAD/public/assets/images/light-illustration.png -------------------------------------------------------------------------------- /app/(auth)/sign-in/[[...sign-in]]/page.tsx: -------------------------------------------------------------------------------- 1 | import { SignIn } from "@clerk/nextjs"; 2 | 3 | export default function Page() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /app/(auth)/sign-up/[[...sign-up]]/page.tsx: -------------------------------------------------------------------------------- 1 | import { SignUp } from "@clerk/nextjs"; 2 | 3 | export default function Page() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "next/core-web-vitals", 4 | "standard", 5 | "plugin:tailwindcss/recommended", 6 | "prettier" 7 | ], 8 | "ignorePatterns": ["/components/ui/*"] 9 | } 10 | -------------------------------------------------------------------------------- /public/assets/icons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/assets/icons/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "editor.formatOnSave": true, 4 | "editor.codeActionsOnSave": { 5 | "source.fixAll.eslint": true, 6 | "source.addMissingImports": true 7 | }, 8 | "[typescriptreact]": { 9 | "editor.defaultFormatter": "esbenp.prettier-vscode" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /public/assets/icons/star.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/assets/icons/tag.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true 11 | }, 12 | "aliases": { 13 | "components": "@/components", 14 | "utils": "@/lib/utils" 15 | } 16 | } -------------------------------------------------------------------------------- /components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { cn } from "@/lib/utils"; 3 | 4 | function Skeleton({ 5 | className, 6 | ...props 7 | }: React.HTMLAttributes) { 8 | return ( 9 |
16 | ); 17 | } 18 | 19 | export { Skeleton }; 20 | -------------------------------------------------------------------------------- /app/(auth)/layout.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import type { Metadata } from "next"; 4 | 5 | export const metadata: Metadata = { 6 | title: "Auth — DevOverflow", 7 | }; 8 | 9 | export default function AuthLayout({ 10 | children, 11 | }: { 12 | children: React.ReactNode; 13 | }) { 14 | return ( 15 |
16 | {children} 17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /public/assets/icons/avatar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | typescript: { 4 | ignoreBuildErrors: true, 5 | }, 6 | experimental: { 7 | mdxRs: true, 8 | serverComponentsExternalPackages: ["mongoose"], 9 | }, 10 | images: { 11 | remotePatterns: [ 12 | { 13 | protocol: "https", 14 | hostname: "*", 15 | }, 16 | { 17 | protocol: "http", 18 | hostname: "*", 19 | }, 20 | ], 21 | }, 22 | }; 23 | 24 | module.exports = nextConfig; 25 | -------------------------------------------------------------------------------- /public/assets/icons/computer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/(root)/tags/[id]/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from "@/components/ui/skeleton"; 2 | 3 | const Loading = () => { 4 | return ( 5 |
6 | 7 | 8 | 9 | 10 |
11 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((item) => ( 12 | 13 | ))} 14 |
15 |
16 | ); 17 | }; 18 | 19 | export default Loading; 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /public/assets/icons/moon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/briefcase.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/arrow-up-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY= 2 | CLERK_SECRET_KEY= 3 | 4 | CLERK_WEBHOOK_SECRET= 5 | 6 | NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in 7 | NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up 8 | NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/ 9 | NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/onboarding 10 | 11 | NEXT_PUBLIC_TINY_MCE_API_KEY= 12 | 13 | MONGODB_URL= 14 | 15 | NEXT_PUBLIC_SERVER_URL= 16 | 17 | OPENAI_API_KEY= 18 | 19 | RAPID_API_KEY= 20 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/mongoose.ts: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | let isConnected: boolean = false; 4 | 5 | export const connectToDatabase = async () => { 6 | mongoose.set("strictQuery", true); 7 | 8 | if (!process.env.MONGODB_URL) 9 | return console.log("Missing environment variable: MONGODB_URL"); 10 | 11 | if (isConnected) return; 12 | 13 | try { 14 | await mongoose.connect(process.env.MONGODB_URL, { 15 | dbName: "DevOverflow", 16 | }); 17 | 18 | isConnected = true; 19 | 20 | console.log("MongoDB is connected"); 21 | } catch (error) { 22 | console.log("MongoDB connection failed", error); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /public/assets/icons/clock-2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/assets/icons/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/assets/icons/hamburger.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/assets/icons/message.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/(root)/collection/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from "@/components/ui/skeleton"; 2 | 3 | const Loading = () => { 4 | return ( 5 |
6 |

Saved Questions

7 | 8 |
9 | 10 | 11 |
12 | 13 |
14 | {[...Array(10)].map((_, i) => ( 15 | 16 | ))} 17 |
18 |
19 | ); 20 | }; 21 | 22 | export default Loading; 23 | -------------------------------------------------------------------------------- /public/assets/icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/(root)/community/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from "@/components/ui/skeleton"; 2 | 3 | const Loading = () => { 4 | return ( 5 |
6 |

All Users

7 | 8 |
9 | 10 | 11 |
12 | 13 |
14 | {[...Array(10)].map((_, i) => ( 15 | 16 | ))} 17 |
18 |
19 | ); 20 | }; 21 | 22 | export default Loading; 23 | -------------------------------------------------------------------------------- /lib/validations.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod"; 2 | 3 | export const QuestionValidation = z.object({ 4 | title: z.string().min(5).max(130), 5 | explanation: z.string().min(20), 6 | tags: z.array(z.string().min(1).max(20)).min(1).max(3), 7 | }); 8 | 9 | export const AnswerValidation = z.object({ 10 | answer: z.string().min(10), 11 | }); 12 | 13 | export const ProfileValidation = z.object({ 14 | name: z.string().min(5).max(50), 15 | username: z.string().min(5).max(50), 16 | bio: z.union([z.string().min(5).max(50), z.literal("")]), 17 | portfolioWebsite: z.union([z.string().url(), z.literal("")]), 18 | location: z.union([z.string().min(5).max(50), z.literal("")]), 19 | }); 20 | -------------------------------------------------------------------------------- /app/(root)/tags/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from "@/components/ui/skeleton"; 2 | 3 | const Loading = () => { 4 | return ( 5 |
6 |

Tags

7 | 8 |
9 | 10 | 11 |
12 | 13 |
14 | {[...Array(10)].map((_, i) => ( 15 | 16 | ))} 17 |
18 |
19 | ); 20 | }; 21 | 22 | export default Loading; 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /database/tag.model.ts: -------------------------------------------------------------------------------- 1 | import { Schema, model, models, Document } from "mongoose"; 2 | 3 | export interface ITag extends Document { 4 | name: string; 5 | description: string; 6 | questions: Schema.Types.ObjectId[]; 7 | followers: Schema.Types.ObjectId[]; 8 | createdOn: Date; 9 | } 10 | 11 | const TagSchema = new Schema({ 12 | name: { type: String, required: true, unique: true }, 13 | description: { type: String, required: true }, 14 | questions: [{ type: Schema.Types.ObjectId, ref: "Question" }], 15 | followers: [{ type: Schema.Types.ObjectId, ref: "User" }], 16 | createdOn: { type: Date, default: Date.now }, 17 | }); 18 | 19 | const Tag = models.Tag || model("Tag", TagSchema); 20 | 21 | export default Tag; 22 | -------------------------------------------------------------------------------- /public/assets/icons/trash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /components/shared/ProfileLink.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import Image from "next/image"; 3 | 4 | interface Props { 5 | imgUrl: string; 6 | title: string; 7 | href?: string; 8 | } 9 | const ProfileLink = ({ imgUrl, title, href }: Props) => { 10 | return ( 11 |
12 | icon 13 | 14 | {href ? ( 15 | 20 | {title} 21 | 22 | ) : ( 23 |

{title}

24 | )} 25 |
26 | ); 27 | }; 28 | 29 | export default ProfileLink; 30 | -------------------------------------------------------------------------------- /middleware.ts: -------------------------------------------------------------------------------- 1 | import { authMiddleware } from "@clerk/nextjs"; 2 | 3 | // This example protects all routes including api/trpc routes 4 | // Please edit this to allow other routes to be public as needed. 5 | // See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your Middleware 6 | export default authMiddleware({ 7 | publicRoutes: [ 8 | "/", 9 | "/api/webhook/clerk", 10 | "/api/rapidapi", 11 | "/question/:id", 12 | "/tags", 13 | "/tags/:id", 14 | "/profile/:id", 15 | "/community", 16 | "/jobs", 17 | ], 18 | ignoredRoutes: ["/api/webhook/clerk", "/api/openai", "/api/rapidapi"], 19 | }); 20 | 21 | export const config = { 22 | matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"], 23 | }; 24 | -------------------------------------------------------------------------------- /components/shared/RenderTag.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | 3 | import { Badge } from "@/components/ui/badge"; 4 | 5 | interface Props { 6 | _id: string; 7 | name: string; 8 | totalQuestions?: number; 9 | showCount?: boolean; 10 | } 11 | 12 | const RenderTag = ({ _id, name, totalQuestions, showCount }: Props) => { 13 | return ( 14 | 15 | 16 | {name} 17 | 18 | {showCount && ( 19 |

{totalQuestions}

20 | )} 21 | 22 | ); 23 | }; 24 | 25 | export default RenderTag; 26 | -------------------------------------------------------------------------------- /components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as LabelPrimitive from "@radix-ui/react-label"; 5 | import { cva, type VariantProps } from "class-variance-authority"; 6 | 7 | import { cn } from "@/lib/utils"; 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ); 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )); 24 | Label.displayName = LabelPrimitive.Root.displayName; 25 | 26 | export { Label }; 27 | -------------------------------------------------------------------------------- /public/assets/icons/star-red.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /public/assets/icons/star-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/(root)/layout.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { Toaster } from "@/components/ui/toaster"; 4 | import Navbar from "@/components/shared/navbar/Navbar"; 5 | import LeftSidebar from "@/components/shared/LeftSidebar"; 6 | import RightSidebar from "@/components/shared/RightSidebar"; 7 | 8 | const Layout = ({ children }: { children: React.ReactNode }) => { 9 | return ( 10 |
11 | 12 |
13 | 14 | 15 |
16 |
{children}
17 |
18 | 19 | 20 |
21 | 22 | 23 |
24 | ); 25 | }; 26 | 27 | export default Layout; 28 | -------------------------------------------------------------------------------- /database/answer.model.ts: -------------------------------------------------------------------------------- 1 | import { Schema, models, model, Document } from "mongoose"; 2 | 3 | export interface IAnswer extends Document { 4 | author: Schema.Types.ObjectId; 5 | question: Schema.Types.ObjectId; 6 | content: string; 7 | upvotes: Schema.Types.ObjectId[]; 8 | downvotes: Schema.Types.ObjectId[]; 9 | createdAt: Date; 10 | } 11 | 12 | const AnswerSchema = new Schema({ 13 | author: { type: Schema.Types.ObjectId, ref: "User", required: true }, 14 | question: { type: Schema.Types.ObjectId, ref: "Question", required: true }, 15 | content: { type: String, required: true }, 16 | upvotes: [{ type: Schema.Types.ObjectId, ref: "User" }], 17 | downvotes: [{ type: Schema.Types.ObjectId, ref: "User" }], 18 | createdAt: { type: Date, default: Date.now }, 19 | }); 20 | 21 | const Answer = models.Answer || model("Answer", AnswerSchema); 22 | 23 | export default Answer; 24 | -------------------------------------------------------------------------------- /components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | 5 | export interface TextareaProps 6 | extends React.TextareaHTMLAttributes {} 7 | 8 | const Textarea = React.forwardRef( 9 | ({ className, ...props }, ref) => { 10 | return ( 11 |