├── README.md ├── .eslintrc.json ├── app ├── api │ ├── auth │ │ └── [...nextauth] │ │ │ └── route.ts │ └── user │ │ ├── getfeed │ │ └── route.ts │ │ ├── createpost │ │ └── route.ts │ │ ├── likepost │ │ └── route.ts │ │ ├── unlikepost │ │ └── route.ts │ │ └── addresourse │ │ └── route.ts ├── favicon.ico ├── opengraph-image.png ├── dashboard │ ├── explore │ │ └── page.tsx │ ├── bookmarks │ │ └── page.tsx │ ├── _page.tsx │ ├── feed │ │ ├── getfeed.ts │ │ ├── page.tsx │ │ └── posts.tsx │ ├── profile │ │ ├── userposts.ts │ │ └── page.tsx │ └── layout.tsx ├── providers.tsx ├── resources │ ├── _page.tsx │ ├── all │ │ ├── getall.ts │ │ └── page.tsx │ ├── aiml │ │ ├── getAIML.ts │ │ └── page.tsx │ ├── devops │ │ ├── getDevOps.ts │ │ └── page.tsx │ ├── webdev │ │ ├── getWebDev.ts │ │ └── page.tsx │ ├── blockchain │ │ ├── getBlockchain.ts │ │ └── page.tsx │ ├── layout.tsx │ ├── resourcecard.tsx │ ├── add │ │ └── page.tsx │ └── sidepanel.tsx ├── community │ └── page.tsx ├── reviews │ └── page.tsx ├── opportunities │ └── page.tsx ├── register │ └── page.tsx ├── signIn │ └── page.tsx ├── domains │ └── page.tsx ├── layout.tsx ├── page.tsx └── globals.css ├── public ├── bg1.png ├── bg2.png ├── bg3.png ├── mit.png ├── nasa.png ├── penn.png ├── oxford.png ├── cambridge.png ├── stanford.png ├── unsightcover.png ├── vercel.svg └── next.svg ├── next.config.mjs ├── prisma ├── migrations │ ├── 20240421202652_init │ │ └── migration.sql │ ├── 20240425162102_init │ │ └── migration.sql │ ├── 20240426192955_init │ │ └── migration.sql │ ├── 20240428205535_added │ │ └── migration.sql │ ├── migration_lock.toml │ ├── 20240425164420_init │ │ └── migration.sql │ ├── 20240428210139_removed │ │ └── migration.sql │ ├── 20240421200914_init │ │ └── migration.sql │ ├── 20240428205649_added │ │ └── migration.sql │ ├── 20240424200156_init │ │ └── migration.sql │ ├── 20240427221532_added │ │ └── migration.sql │ ├── 20240427225515_modefied │ │ └── migration.sql │ ├── 20240424202227_init │ │ └── migration.sql │ ├── 20240424183057_init │ │ └── migration.sql │ ├── 20240424202049_init │ │ └── migration.sql │ └── 20240421200423_init │ │ └── migration.sql └── schema.prisma ├── postcss.config.mjs ├── .env.example ├── components ├── user │ ├── resources.tsx │ ├── reviews.tsx │ ├── opportunity.tsx │ ├── community.tsx │ ├── throttle.ts │ ├── profile.tsx │ └── postPanel.tsx ├── Update.tsx ├── auth │ ├── signoutBtn.tsx │ ├── sideSection.tsx │ ├── signinWithEmail.tsx │ └── registerWithCred.tsx ├── ui │ ├── label.tsx │ ├── input.tsx │ ├── toaster.tsx │ ├── sonner.tsx │ ├── button.tsx │ ├── card.tsx │ ├── use-toast.ts │ ├── form.tsx │ └── toast.tsx ├── mainpage │ ├── featureCard.tsx │ ├── mainpagetitle.tsx │ ├── imagebanner.tsx │ ├── intro.tsx │ ├── mainpage.tsx │ └── footer.tsx ├── underdevelopment.tsx ├── reveal.tsx ├── postCard.tsx ├── explorebar.tsx ├── landing.tsx ├── navbar.tsx └── sidebar.tsx ├── lib ├── utils.ts ├── db.ts ├── resend.ts └── token.ts ├── components.json ├── schema └── index.ts ├── .gitignore ├── utils ├── verification-token.ts └── user.ts ├── auth.config.ts ├── tsconfig.json ├── auth.ts ├── actions ├── register.ts └── login.ts ├── package.json ├── config └── site-config.ts └── tailwind.config.ts /README.md: -------------------------------------------------------------------------------- 1 | ## under development 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | export { GET, POST } from "@/auth"; 2 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kartikver15gr8/Unsight/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /public/bg1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kartikver15gr8/Unsight/HEAD/public/bg1.png -------------------------------------------------------------------------------- /public/bg2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kartikver15gr8/Unsight/HEAD/public/bg2.png -------------------------------------------------------------------------------- /public/bg3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kartikver15gr8/Unsight/HEAD/public/bg3.png -------------------------------------------------------------------------------- /public/mit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kartikver15gr8/Unsight/HEAD/public/mit.png -------------------------------------------------------------------------------- /public/nasa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kartikver15gr8/Unsight/HEAD/public/nasa.png -------------------------------------------------------------------------------- /public/penn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kartikver15gr8/Unsight/HEAD/public/penn.png -------------------------------------------------------------------------------- /public/oxford.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kartikver15gr8/Unsight/HEAD/public/oxford.png -------------------------------------------------------------------------------- /public/cambridge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kartikver15gr8/Unsight/HEAD/public/cambridge.png -------------------------------------------------------------------------------- /public/stanford.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kartikver15gr8/Unsight/HEAD/public/stanford.png -------------------------------------------------------------------------------- /app/opengraph-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kartikver15gr8/Unsight/HEAD/app/opengraph-image.png -------------------------------------------------------------------------------- /public/unsightcover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kartikver15gr8/Unsight/HEAD/public/unsightcover.png -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /prisma/migrations/20240421202652_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "User" ADD COLUMN "password" TEXT; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20240425162102_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Posts" ADD COLUMN "userImg" TEXT; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20240426192955_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Posts" ALTER COLUMN "like" SET DEFAULT 0; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20240428205535_added/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Posts" ADD COLUMN "thumbnailUrl" TEXT; 3 | -------------------------------------------------------------------------------- /app/dashboard/explore/page.tsx: -------------------------------------------------------------------------------- 1 | import Landing from "@/components/landing"; 2 | 3 | export default function Explore() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /app/dashboard/bookmarks/page.tsx: -------------------------------------------------------------------------------- 1 | import Landing from "@/components/landing"; 2 | 3 | export default function Bookmarks() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | DATABASE_URL= 2 | EMAIL_SERVER_HOST= 3 | EMAIL_SERVER_PORT= 4 | EMAIL_SERVER_USER= 5 | EMAIL_SERVER_PASSWORD= 6 | EMAIL_FROM= 7 | RESEND_API_KEY= 8 | AUTH_SECRET= 9 | AUTH_RESEND_KEY= -------------------------------------------------------------------------------- /components/user/resources.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | export default function ResourcesPage() { 3 | return ( 4 |
5 |
Resources
6 |
7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /components/user/reviews.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | export default function ReviewPage() { 3 | return ( 4 |
5 |
Reviews
6 |
7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /components/user/opportunity.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | export default function OpportunityPage() { 3 | return ( 4 |
5 |
Opportunites
6 |
7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /components/user/community.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | export default function CommunityPage() { 4 | return ( 5 |
6 |
Community
7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /prisma/migrations/20240425164420_init/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `userImg` on the `Posts` table. All the data in the column will be lost. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "Posts" DROP COLUMN "userImg"; 9 | -------------------------------------------------------------------------------- /app/providers.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import { SessionProvider } from "next-auth/react"; 5 | 6 | export const Providers = ({ children }: { children: React.ReactNode }) => { 7 | return {children}; 8 | }; 9 | -------------------------------------------------------------------------------- /app/resources/_page.tsx: -------------------------------------------------------------------------------- 1 | import Navbar from "@/components/navbar"; 2 | import ResourcesPage from "@/components/user/resources"; 3 | export default function Resources() { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /prisma/migrations/20240428210139_removed/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `thumbnailUrl` on the `Resources` table. All the data in the column will be lost. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "Resources" DROP COLUMN "thumbnailUrl"; 9 | -------------------------------------------------------------------------------- /lib/db.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | 3 | declare global { 4 | var prisma: PrismaClient | undefined; 5 | } 6 | 7 | export const prisma: PrismaClient = global.prisma || new PrismaClient(); 8 | 9 | if (process.env.NODE_ENV === "development") global.prisma = prisma; 10 | -------------------------------------------------------------------------------- /app/community/page.tsx: -------------------------------------------------------------------------------- 1 | import Navbar from "@/components/navbar"; 2 | import CommunityPage from "@/components/user/community"; 3 | export default function Community() { 4 | return ( 5 |
6 | 7 | 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /prisma/migrations/20240421200914_init/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - A unique constraint covering the columns `[token]` on the table `VerificationToken` will be added. If there are existing duplicate values, this will fail. 5 | 6 | */ 7 | -- CreateIndex 8 | CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token"); 9 | -------------------------------------------------------------------------------- /app/reviews/page.tsx: -------------------------------------------------------------------------------- 1 | import Navbar from "@/components/navbar"; 2 | import UnderDevelopment from "@/components/underdevelopment"; 3 | import ReviewPage from "@/components/user/reviews"; 4 | export default function Resources() { 5 | return ( 6 |
7 | 8 | 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /prisma/migrations/20240428205649_added/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `thumbnailUrl` on the `Posts` table. All the data in the column will be lost. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "Posts" DROP COLUMN "thumbnailUrl"; 9 | 10 | -- AlterTable 11 | ALTER TABLE "Resources" ADD COLUMN "thumbnailUrl" TEXT; 12 | -------------------------------------------------------------------------------- /app/opportunities/page.tsx: -------------------------------------------------------------------------------- 1 | import Navbar from "@/components/navbar"; 2 | import UnderDevelopment from "@/components/underdevelopment"; 3 | import OpportunityPage from "@/components/user/opportunity"; 4 | export default function Opportunites() { 5 | return ( 6 |
7 | 8 | 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /app/register/page.tsx: -------------------------------------------------------------------------------- 1 | import RegisterCredentialsForm from "@/components/auth/registerWithCred"; 2 | import { auth } from "@/auth"; 3 | import { redirect } from "next/navigation"; 4 | const RegisterPage = async () => { 5 | const session = await auth(); 6 | 7 | return
{session ? redirect("/") : }
; 8 | }; 9 | 10 | export default RegisterPage; 11 | -------------------------------------------------------------------------------- /app/signIn/page.tsx: -------------------------------------------------------------------------------- 1 | import SignInForm from "@/components/auth/signinWithEmail"; 2 | import React from "react"; 3 | import { redirect } from "next/navigation"; 4 | import { auth } from "@/auth"; 5 | 6 | const SignInPage = async () => { 7 | const session = await auth(); 8 | 9 | return
{session ? redirect("/") : }
; 10 | }; 11 | 12 | export default SignInPage; 13 | -------------------------------------------------------------------------------- /prisma/migrations/20240424200156_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "Posts" DROP CONSTRAINT "Posts_commentId_fkey"; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Posts" ALTER COLUMN "commentId" DROP NOT NULL; 6 | 7 | -- AddForeignKey 8 | ALTER TABLE "Posts" ADD CONSTRAINT "Posts_commentId_fkey" FOREIGN KEY ("commentId") REFERENCES "Comments"("id") ON DELETE SET NULL ON UPDATE CASCADE; 9 | -------------------------------------------------------------------------------- /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.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /components/Update.tsx: -------------------------------------------------------------------------------- 1 | export default function Update() { 2 | return ( 3 |
4 |

5 | This Product is under development, Stay tuned for updates! 6 |

7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /app/domains/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { universities } from "@/actions/domains"; 3 | import { useEffect, useState } from "react"; 4 | 5 | export default function Domains() { 6 | const [domains, setDomains] = useState(); 7 | useEffect(() => { 8 | setDomains(universities); 9 | }, []); 10 | return ( 11 |
12 |
{JSON.stringify(domains)}
13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /components/auth/signoutBtn.tsx: -------------------------------------------------------------------------------- 1 | import { signOut } from "@/auth"; 2 | import { Button } from "@/components/ui/button"; 3 | 4 | const SignoutButton = () => { 5 | return ( 6 |
7 |
{ 9 | "use server"; 10 | await signOut(); 11 | }} 12 | > 13 | 14 |
15 |
16 | ); 17 | }; 18 | 19 | export default SignoutButton; 20 | -------------------------------------------------------------------------------- /components/user/throttle.ts: -------------------------------------------------------------------------------- 1 | export const throttle = any>( 2 | func: T, 3 | delay: number 4 | ): ((...args: Parameters) => ReturnType | undefined) => { 5 | let lastCall = 0; 6 | return (...args: Parameters): ReturnType | undefined => { 7 | const now = new Date().getTime(); 8 | if (now - lastCall < delay) { 9 | return; 10 | } 11 | lastCall = now; 12 | return func(...args); 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /app/resources/all/getall.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from "@/lib/db"; 2 | export const GetAllResources = async () => { 3 | const resources = await prisma.resources.findMany({}); 4 | if (resources) { 5 | return resources; 6 | } 7 | return [ 8 | { 9 | id: 1, 10 | title: "string", 11 | description: "string", 12 | userId: "string", 13 | categary: "string", 14 | upvotes: [""], 15 | resourceLink: "string", 16 | }, 17 | ]; 18 | }; 19 | -------------------------------------------------------------------------------- /schema/index.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod"; 2 | 3 | export const LoginSchema = z.object({ 4 | email: z.string().email({ 5 | message: "Please enter a valid email address", 6 | }), 7 | }); 8 | 9 | export const RegisterSchema = z.object({ 10 | email: z.string().email({ 11 | message: "Email is required", 12 | }), 13 | password: z.string().min(6, { 14 | message: "Minimum 6 characters required", 15 | }), 16 | name: z.string().min(2, { 17 | message: "Name is required", 18 | }), 19 | }); 20 | -------------------------------------------------------------------------------- /app/dashboard/_page.tsx: -------------------------------------------------------------------------------- 1 | import { auth, signOut } from "@/auth"; 2 | import SignoutButton from "@/components/auth/signoutBtn"; 3 | import { redirect } from "next/navigation"; 4 | import Landing from "@/components/landing"; 5 | import Sidebar from "@/components/sidebar"; 6 | import Navbar from "@/components/navbar"; 7 | import Explorebar from "@/components/explorebar"; 8 | 9 | const DashboardPage = async () => { 10 | const session = await auth(); 11 | 12 | return ; 13 | }; 14 | 15 | export default DashboardPage; 16 | -------------------------------------------------------------------------------- /app/dashboard/feed/getfeed.ts: -------------------------------------------------------------------------------- 1 | import { auth } from "@/auth"; 2 | import { prisma } from "@/lib/db"; 3 | export async function GetFeed() { 4 | const session = await auth(); 5 | const userEmail = session?.user?.email; 6 | 7 | if (userEmail) { 8 | const feed = await prisma.posts.findMany({}); 9 | if (feed) { 10 | return feed; 11 | } 12 | } 13 | return [ 14 | { 15 | id: 1, 16 | description: "string", 17 | imgUrl: "string", 18 | userId: "string", 19 | like: 1, 20 | }, 21 | ]; 22 | } 23 | -------------------------------------------------------------------------------- /.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 | .env 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /app/api/user/getfeed/route.ts: -------------------------------------------------------------------------------- 1 | import { auth } from "@/auth"; 2 | import { prisma } from "@/lib/db"; 3 | export async function GET() { 4 | const session = await auth(); 5 | const userEmail = session?.user?.email; 6 | 7 | if (userEmail) { 8 | const feed = await prisma.posts.findMany({}); 9 | if (feed) { 10 | return Response.json(feed); 11 | } 12 | } 13 | return Response.json([ 14 | { 15 | id: 1, 16 | description: "string", 17 | imgUrl: "string", 18 | userId: "string", 19 | like: 1, 20 | }, 21 | ]); 22 | } 23 | -------------------------------------------------------------------------------- /prisma/migrations/20240427221532_added/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Resources" ( 3 | "id" SERIAL NOT NULL, 4 | "title" TEXT NOT NULL, 5 | "description" TEXT NOT NULL, 6 | "userId" TEXT NOT NULL, 7 | "catogary" TEXT NOT NULL, 8 | "upvotes" TEXT[], 9 | "resourceLink" TEXT NOT NULL, 10 | 11 | CONSTRAINT "Resources_pkey" PRIMARY KEY ("id") 12 | ); 13 | 14 | -- AddForeignKey 15 | ALTER TABLE "Resources" ADD CONSTRAINT "Resources_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 16 | -------------------------------------------------------------------------------- /prisma/migrations/20240427225515_modefied/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `catogary` on the `Resources` table. All the data in the column will be lost. 5 | - You are about to drop the column `upvotes` on the `Resources` table. All the data in the column will be lost. 6 | - Added the required column `categary` to the `Resources` table without a default value. This is not possible if the table is not empty. 7 | 8 | */ 9 | -- AlterTable 10 | ALTER TABLE "Resources" DROP COLUMN "catogary", 11 | DROP COLUMN "upvotes", 12 | ADD COLUMN "categary" TEXT NOT NULL; 13 | -------------------------------------------------------------------------------- /prisma/migrations/20240424202227_init/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `commentId` on the `Posts` table. All the data in the column will be lost. 5 | - You are about to drop the `Comments` table. If the table is not empty, all the data it contains will be lost. 6 | 7 | */ 8 | -- DropForeignKey 9 | ALTER TABLE "Comments" DROP CONSTRAINT "Comments_userId_fkey"; 10 | 11 | -- DropForeignKey 12 | ALTER TABLE "Posts" DROP CONSTRAINT "Posts_commentId_fkey"; 13 | 14 | -- AlterTable 15 | ALTER TABLE "Posts" DROP COLUMN "commentId"; 16 | 17 | -- DropTable 18 | DROP TABLE "Comments"; 19 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | import { Providers } from "./providers"; 5 | import { siteConfig } from "@/config/site-config"; 6 | 7 | const inter = Inter({ subsets: ["latin"] }); 8 | 9 | export const metadata: Metadata = siteConfig; 10 | 11 | export default function RootLayout({ 12 | children, 13 | }: Readonly<{ 14 | children: React.ReactNode; 15 | }>) { 16 | return ( 17 | 18 | 19 | {children} 20 | 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /utils/verification-token.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from "@/lib/db"; 2 | export const getVerificationTokenByEmail = async (email: string) => { 3 | const verificationToken = await prisma.verificationToken.findFirst({ 4 | where: { 5 | email, 6 | }, 7 | }); 8 | 9 | return verificationToken; 10 | }; 11 | 12 | export const getVerificationTokenByToken = async (token: string) => { 13 | try { 14 | const verificationToken = await prisma.verificationToken.findUnique({ 15 | where: { 16 | token, 17 | }, 18 | }); 19 | 20 | return verificationToken; 21 | } catch (error) { 22 | return null; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /utils/user.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from "@/lib/db"; 2 | 3 | export const getUserByEmail = async (email: string) => { 4 | try { 5 | const user = await prisma.user.findUnique({ 6 | where: { 7 | email, 8 | }, 9 | }); 10 | if (user) { 11 | return user; 12 | } 13 | } catch (error) { 14 | return error; 15 | } 16 | }; 17 | 18 | export const getUserById = async (id: string) => { 19 | try { 20 | const user = prisma.user.findUnique({ 21 | where: { 22 | id, 23 | }, 24 | }); 25 | if (user) { 26 | return user; 27 | } 28 | } catch (error) { 29 | return error; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /auth.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextAuthConfig } from "next-auth"; 2 | import EmailProvider from "next-auth/providers/nodemailer"; 3 | import Resend from "next-auth/providers/resend"; 4 | 5 | export default { 6 | providers: [Resend({ from: "no-reply@unsight.club" })], 7 | } satisfies NextAuthConfig; 8 | 9 | // EmailProvider({ 10 | // id: "email", 11 | // name: "email", 12 | // server: { 13 | // host: process.env.EMAIL_SERVER_HOST, 14 | // port: process.env.EMAIL_SERVER_PORT, 15 | // auth: { 16 | // user: process.env.EMAIL_SERVER_USER, 17 | // pass: process.env.EMAIL_SERVER_PASSWORD, 18 | // }, 19 | // }, 20 | // from: process.env.EMAIL_FROM, 21 | // }), 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /app/resources/aiml/getAIML.ts: -------------------------------------------------------------------------------- 1 | import { auth } from "@/auth"; 2 | import { prisma } from "@/lib/db"; 3 | 4 | export async function GetAIML() { 5 | const session = await auth(); 6 | const userEmail = session?.user?.email; 7 | if (userEmail) { 8 | const devOpsResources = await prisma.resources.findMany({ 9 | where: { 10 | categary: "aiml", 11 | }, 12 | }); 13 | if (devOpsResources) { 14 | return devOpsResources; 15 | } 16 | } 17 | return [ 18 | { 19 | id: 1, 20 | title: "string", 21 | description: "string", 22 | userId: "string", 23 | catogary: "aiml", 24 | upvotes: [""], 25 | resourceLink: "string", 26 | }, 27 | ]; 28 | } 29 | -------------------------------------------------------------------------------- /app/resources/devops/getDevOps.ts: -------------------------------------------------------------------------------- 1 | import { auth } from "@/auth"; 2 | import { prisma } from "@/lib/db"; 3 | 4 | export async function GetDevOps() { 5 | const session = await auth(); 6 | const userEmail = session?.user?.email; 7 | if (userEmail) { 8 | const devOpsResources = await prisma.resources.findMany({ 9 | where: { 10 | categary: "devops", 11 | }, 12 | }); 13 | if (devOpsResources) { 14 | return devOpsResources; 15 | } 16 | } 17 | return [ 18 | { 19 | id: 1, 20 | title: "string", 21 | description: "string", 22 | userId: "string", 23 | catogary: "devops", 24 | upvotes: [""], 25 | resourceLink: "string", 26 | }, 27 | ]; 28 | } 29 | -------------------------------------------------------------------------------- /app/resources/webdev/getWebDev.ts: -------------------------------------------------------------------------------- 1 | import { auth } from "@/auth"; 2 | import { prisma } from "@/lib/db"; 3 | 4 | export const GetWebDev = async () => { 5 | const session = await auth(); 6 | const userEmail = session?.user?.email; 7 | if (userEmail) { 8 | const webDevResources = await prisma.resources.findMany({ 9 | where: { 10 | categary: "webdev", 11 | }, 12 | }); 13 | if (webDevResources) { 14 | return webDevResources; 15 | } 16 | } 17 | return [ 18 | { 19 | id: 1, 20 | title: "string", 21 | description: "string", 22 | userId: "string", 23 | catogary: "webdev", 24 | upvotes: [""], 25 | resourceLink: "string", 26 | }, 27 | ]; 28 | }; 29 | -------------------------------------------------------------------------------- /auth.ts: -------------------------------------------------------------------------------- 1 | import NextAuth from "next-auth"; 2 | import authConfig from "./auth.config"; 3 | import { PrismaAdapter } from "@auth/prisma-adapter"; 4 | import { prisma } from "@/lib/db"; 5 | 6 | export const { 7 | handlers: { GET, POST }, 8 | auth, 9 | signIn, 10 | signOut, 11 | } = NextAuth({ 12 | callbacks: { 13 | async session({ token, session }) { 14 | console.log("Session Token", token); 15 | 16 | return session; 17 | }, 18 | async jwt({ token }) { 19 | console.log("JWT Token", token); 20 | return token; 21 | }, 22 | }, 23 | adapter: PrismaAdapter(prisma), 24 | session: { 25 | strategy: "jwt", 26 | }, 27 | ...authConfig, 28 | pages: { 29 | signIn: "/signIn", 30 | }, 31 | }); 32 | -------------------------------------------------------------------------------- /app/resources/blockchain/getBlockchain.ts: -------------------------------------------------------------------------------- 1 | import { auth } from "@/auth"; 2 | import { prisma } from "@/lib/db"; 3 | 4 | export async function GetBlockchain() { 5 | const session = await auth(); 6 | const userEmail = session?.user?.email; 7 | if (userEmail) { 8 | const devOpsResources = await prisma.resources.findMany({ 9 | where: { 10 | categary: "blockchain", 11 | }, 12 | }); 13 | if (devOpsResources) { 14 | return devOpsResources; 15 | } 16 | } 17 | return [ 18 | { 19 | id: 1, 20 | title: "string", 21 | description: "string", 22 | userId: "string", 23 | catogary: "blockchain", 24 | upvotes: [""], 25 | resourceLink: "string", 26 | }, 27 | ]; 28 | } 29 | -------------------------------------------------------------------------------- /lib/resend.ts: -------------------------------------------------------------------------------- 1 | import { Resend } from "resend"; 2 | 3 | const resend = new Resend(process.env.RESEND_API_KEY); 4 | 5 | export const sendVerificationEmail = async (email: string, token: string) => { 6 | // const confirmLink = `http://localhost:3000/auth/new-verification?token=${token}`; 7 | const confirmLink = 8 | process.env.NODE_ENV === "development" 9 | ? `http://localhost:3000/auth/new-verification?token=${token}` 10 | : `https://www.unsight.club/auth/new-verification?token=${token}`; 11 | 12 | await resend.emails.send({ 13 | from: "onboarding@unsight.club", 14 | to: email, 15 | subject: "Confirm your email", 16 | html: `

Click here to confirm your email.

`, 17 | }); 18 | }; 19 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Image from "next/image"; 3 | import { Button } from "@/components/ui/button"; 4 | import Link from "next/link"; 5 | import { auth } from "@/auth"; 6 | import { signOut } from "next-auth/react"; 7 | import { useSession } from "next-auth/react"; 8 | import Navbar from "@/components/navbar"; 9 | import Landing from "@/components/landing"; 10 | import Mainpage from "@/components/mainpage/mainpage"; 11 | import Update from "@/components/Update"; 12 | 13 | export default function Home() { 14 | // const session = useSession(); 15 | return ( 16 |
17 | {/*
18 | 19 |
*/} 20 | 21 | 22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /app/dashboard/profile/userposts.ts: -------------------------------------------------------------------------------- 1 | import { auth } from "@/auth"; 2 | import { prisma } from "@/lib/db"; 3 | 4 | export default async function UserPosts() { 5 | const session = await auth(); 6 | const userEmail = session?.user?.email; 7 | 8 | if (userEmail) { 9 | const user = await prisma.user.findUnique({ 10 | where: { 11 | email: userEmail, 12 | }, 13 | }); 14 | if (user) { 15 | const posts = await prisma.posts.findMany({ 16 | where: { 17 | userId: user.id, 18 | }, 19 | }); 20 | if (posts) { 21 | return posts; 22 | } 23 | } 24 | } 25 | return [ 26 | { 27 | id: 1, 28 | description: "string", 29 | imgUrl: "null", 30 | userId: "string", 31 | like: 1, 32 | }, 33 | ]; 34 | } 35 | -------------------------------------------------------------------------------- /app/resources/aiml/page.tsx: -------------------------------------------------------------------------------- 1 | import ResourceCard from "../resourcecard"; 2 | import { GetAIML } from "./getAIML"; 3 | import Reveal from "@/components/reveal"; 4 | export default async function AIML() { 5 | const AIMLResources = await GetAIML(); 6 | return ( 7 |
8 | {AIMLResources.map((e, key) => { 9 | return ( 10 | 11 | 20 | 21 | ); 22 | })} 23 |
24 | ); 25 | } 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 | -------------------------------------------------------------------------------- /app/resources/all/page.tsx: -------------------------------------------------------------------------------- 1 | import ResourceCard from "../resourcecard"; 2 | import { GetAllResources } from "./getall"; 3 | import Reveal from "@/components/reveal"; 4 | export default async function AllResources() { 5 | const resources = await GetAllResources(); 6 | return ( 7 |
8 | {resources.map((e, key) => { 9 | return ( 10 | 11 | 20 | 21 | ); 22 | })} 23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /lib/token.ts: -------------------------------------------------------------------------------- 1 | import { getVerificationTokenByEmail } from "@/utils/verification-token"; 2 | import { v4 as uuidv4 } from "uuid"; 3 | import { prisma } from "@/lib/db"; 4 | export const generateVerificationToken = async (email: string) => { 5 | const token = uuidv4(); 6 | const expires = new Date(new Date().getTime() + 2 * 60 * 60 * 1000); // 2 hours 7 | 8 | const existingToken = await getVerificationTokenByEmail(email); 9 | 10 | if (existingToken) { 11 | await prisma.verificationToken.delete({ 12 | where: { 13 | identifier_token: existingToken, 14 | }, 15 | }); 16 | } 17 | 18 | const verificationToken = await prisma.verificationToken.create({ 19 | data: { 20 | identifier: token, 21 | email, 22 | token, 23 | expires, 24 | }, 25 | }); 26 | 27 | return verificationToken; 28 | }; 29 | -------------------------------------------------------------------------------- /app/resources/devops/page.tsx: -------------------------------------------------------------------------------- 1 | import { GetDevOps } from "./getDevOps"; 2 | import ResourceCard from "../resourcecard"; 3 | import Reveal from "@/components/reveal"; 4 | 5 | export default async function Devops() { 6 | const DevOpsResources = await GetDevOps(); 7 | return ( 8 |
9 | {DevOpsResources.map((e, key) => { 10 | return ( 11 | 12 | 21 | 22 | ); 23 | })} 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /app/resources/webdev/page.tsx: -------------------------------------------------------------------------------- 1 | import Reveal from "@/components/reveal"; 2 | import ResourceCard from "../resourcecard"; 3 | import { GetWebDev } from "./getWebDev"; 4 | 5 | export default async function WebDev() { 6 | const WebDevResources = await GetWebDev(); 7 | return ( 8 |
9 | {WebDevResources.map((e, key) => { 10 | return ( 11 | 12 | 21 | 22 | ); 23 | })} 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /app/resources/blockchain/page.tsx: -------------------------------------------------------------------------------- 1 | import { GetBlockchain } from "./getBlockchain"; 2 | import ResourceCard from "../resourcecard"; 3 | import Reveal from "@/components/reveal"; 4 | 5 | export default async function Blockchain() { 6 | const Web3Resources = await GetBlockchain(); 7 | return ( 8 |
9 | {Web3Resources.map((e, key) => { 10 | return ( 11 | 12 | 21 | 22 | ); 23 | })} 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /components/mainpage/featureCard.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | export default function FeatureCard({ 4 | topText, 5 | svg, 6 | description, 7 | }: { 8 | topText: string; 9 | svg: React.ReactNode; 10 | description: string; 11 | }) { 12 | return ( 13 |
14 |

15 | {topText} 16 |

17 |
18 |
{svg}
19 |

20 | {description} 21 |

22 |
23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /app/dashboard/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter, Kanit } from "next/font/google"; 3 | import Sidebar from "@/components/sidebar"; 4 | import Landing from "@/components/landing"; 5 | import Explorebar from "@/components/explorebar"; 6 | import Navbar from "@/components/navbar"; 7 | 8 | const inter = Inter({ subsets: ["latin"] }); 9 | const kanit = Kanit({ subsets: ["latin"], weight: "400" }); 10 | 11 | export const metadata: Metadata = { 12 | title: "Unsight", 13 | description: "Share your thoughts freely", 14 | }; 15 | 16 | export default function RootLayout({ 17 | children, 18 | }: Readonly<{ 19 | children: React.ReactNode; 20 | }>) { 21 | return ( 22 | <> 23 | 24 |
25 | 26 | {children} 27 | 28 |
29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ) 21 | } 22 | ) 23 | Input.displayName = "Input" 24 | 25 | export { Input } 26 | -------------------------------------------------------------------------------- /app/api/user/createpost/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest } from "next/server"; 2 | import { auth } from "@/auth"; 3 | import { prisma } from "@/lib/db"; 4 | 5 | export async function POST(req: NextRequest) { 6 | const session = await auth(); 7 | const { description } = await req.json(); 8 | const userEmail = session?.user?.email; 9 | 10 | if (userEmail) { 11 | const user = await prisma.user.findUnique({ 12 | where: { 13 | email: userEmail, 14 | }, 15 | }); 16 | if (user) { 17 | const post = await prisma.posts.create({ 18 | data: { 19 | description: description, 20 | userId: user.id, 21 | }, 22 | }); 23 | if (post) { 24 | return Response.json(post); 25 | } 26 | } 27 | } 28 | return Response.json({ 29 | id: "string", 30 | description: "string", 31 | imgUrl: "string ", 32 | userId: "string", 33 | like: "number ", 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /components/underdevelopment.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Button } from "./ui/button"; 3 | import Link from "next/link"; 4 | 5 | export default function UnderDevelopment() { 6 | return ( 7 |
8 |
9 |
10 | This page is under development! 11 |
12 |
13 | Soon You will see content over here, Till then please explore our 14 | other pages 15 |
16 | 17 | 20 | 21 |
22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /components/ui/toaster.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { 4 | Toast, 5 | ToastClose, 6 | ToastDescription, 7 | ToastProvider, 8 | ToastTitle, 9 | ToastViewport, 10 | } from "@/components/ui/toast" 11 | import { useToast } from "@/components/ui/use-toast" 12 | 13 | export function Toaster() { 14 | const { toasts } = useToast() 15 | 16 | return ( 17 | 18 | {toasts.map(function ({ id, title, description, action, ...props }) { 19 | return ( 20 | 21 |
22 | {title && {title}} 23 | {description && ( 24 | {description} 25 | )} 26 |
27 | {action} 28 | 29 |
30 | ) 31 | })} 32 | 33 |
34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /app/api/user/likepost/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest } from "next/server"; 2 | import { auth } from "@/auth"; 3 | import { prisma } from "@/lib/db"; 4 | export async function POST(req: NextRequest) { 5 | const session = await auth(); 6 | const userEmail = session?.user?.email; 7 | const { postId } = await req.json(); 8 | 9 | if (userEmail) { 10 | const user = await prisma.user.findUnique({ 11 | where: { 12 | email: userEmail, 13 | }, 14 | }); 15 | if (user) { 16 | const post = await prisma.posts.findFirst({ 17 | where: { 18 | id: postId, 19 | }, 20 | }); 21 | if (post) { 22 | const likes = post.like; 23 | await prisma.posts.update({ 24 | where: { 25 | id: postId, 26 | }, 27 | data: { 28 | like: likes ? likes + 1 : 1, 29 | }, 30 | }); 31 | return Response.json(likes); 32 | } 33 | } 34 | } 35 | return Response.json(1); 36 | } 37 | -------------------------------------------------------------------------------- /app/api/user/unlikepost/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest } from "next/server"; 2 | import { auth } from "@/auth"; 3 | import { prisma } from "@/lib/db"; 4 | export async function POST(req: NextRequest) { 5 | const session = await auth(); 6 | const userEmail = session?.user?.email; 7 | const { postId } = await req.json(); 8 | 9 | if (userEmail) { 10 | const user = await prisma.user.findUnique({ 11 | where: { 12 | email: userEmail, 13 | }, 14 | }); 15 | if (user) { 16 | const post = await prisma.posts.findFirst({ 17 | where: { 18 | id: postId, 19 | }, 20 | }); 21 | if (post) { 22 | const likes = post.like; 23 | await prisma.posts.update({ 24 | where: { 25 | id: postId, 26 | }, 27 | data: { 28 | like: likes ? likes - 1 : 0, 29 | }, 30 | }); 31 | return Response.json(likes); 32 | } 33 | } 34 | } 35 | return Response.json(1); 36 | } 37 | -------------------------------------------------------------------------------- /app/resources/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter, Kanit } from "next/font/google"; 3 | import Sidebar from "@/components/sidebar"; 4 | import Landing from "@/components/landing"; 5 | import Explorebar from "@/components/explorebar"; 6 | import Navbar from "@/components/navbar"; 7 | import SidePanel from "./sidepanel"; 8 | 9 | const inter = Inter({ subsets: ["latin"] }); 10 | const kanit = Kanit({ subsets: ["latin"], weight: "400" }); 11 | 12 | export const metadata: Metadata = { 13 | title: "Unsight", 14 | description: "Share your thoughts freely", 15 | }; 16 | 17 | export default function RootLayout({ 18 | children, 19 | }: Readonly<{ 20 | children: React.ReactNode; 21 | }>) { 22 | return ( 23 | <> 24 | 25 |
26 | 27 |
{children}
28 | 29 |
30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /components/ui/sonner.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useTheme } from "next-themes" 4 | import { Toaster as Sonner } from "sonner" 5 | 6 | type ToasterProps = React.ComponentProps 7 | 8 | const Toaster = ({ ...props }: ToasterProps) => { 9 | const { theme = "system" } = useTheme() 10 | 11 | return ( 12 | 28 | ) 29 | } 30 | 31 | export { Toaster } 32 | -------------------------------------------------------------------------------- /components/reveal.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useEffect, useRef } from "react"; 3 | import { motion, useInView, useAnimation } from "framer-motion"; 4 | 5 | interface Props { 6 | children: JSX.Element; 7 | width?: "fit-content" | "100%"; 8 | } 9 | 10 | export default function Reveal({ children, width = "fit-content" }: Props) { 11 | const ref = useRef(null); 12 | const isInView = useInView(ref, { once: true }); 13 | const mainControls = useAnimation(); 14 | 15 | useEffect(() => { 16 | if (isInView) { 17 | mainControls.start("visible"); 18 | } 19 | }, [isInView, mainControls]); 20 | return ( 21 |
22 | 31 | {children} 32 | 33 |
34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /prisma/migrations/20240424183057_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Posts" ( 3 | "id" TEXT NOT NULL, 4 | "description" TEXT NOT NULL, 5 | "imgUrl" TEXT, 6 | "userId" TEXT NOT NULL, 7 | "like" INTEGER, 8 | "commentId" TEXT NOT NULL, 9 | 10 | CONSTRAINT "Posts_pkey" PRIMARY KEY ("id") 11 | ); 12 | 13 | -- CreateTable 14 | CREATE TABLE "Comments" ( 15 | "id" TEXT NOT NULL, 16 | "content" TEXT NOT NULL, 17 | "imgUrl" TEXT, 18 | "userId" TEXT NOT NULL, 19 | "like" INTEGER, 20 | 21 | CONSTRAINT "Comments_pkey" PRIMARY KEY ("id") 22 | ); 23 | 24 | -- AddForeignKey 25 | ALTER TABLE "Posts" ADD CONSTRAINT "Posts_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 26 | 27 | -- AddForeignKey 28 | ALTER TABLE "Posts" ADD CONSTRAINT "Posts_commentId_fkey" FOREIGN KEY ("commentId") REFERENCES "Comments"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 29 | 30 | -- AddForeignKey 31 | ALTER TABLE "Comments" ADD CONSTRAINT "Comments_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 32 | -------------------------------------------------------------------------------- /app/api/user/addresourse/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest } from "next/server"; 2 | import { auth } from "@/auth"; 3 | import { prisma } from "@/lib/db"; 4 | 5 | export async function POST(req: NextRequest) { 6 | const session = await auth(); 7 | 8 | const { title, description, categary, resourceLink } = await req.json(); 9 | 10 | const userEmail = session?.user?.email; 11 | 12 | if (userEmail) { 13 | const user = await prisma.user.findUnique({ 14 | where: { 15 | email: userEmail, 16 | }, 17 | }); 18 | if (user) { 19 | const post = await prisma.resources.create({ 20 | data: { 21 | title: title, 22 | description: description, 23 | userId: user.id, 24 | categary: categary, 25 | resourceLink: resourceLink, 26 | }, 27 | }); 28 | if (post) { 29 | return Response.json(post); 30 | } 31 | } 32 | } 33 | return Response.json({ 34 | id: 1, 35 | title: "string", 36 | description: "string", 37 | userId: "string", 38 | catogary: "string", 39 | upvotes: [""], 40 | resourceLink: "string", 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /app/dashboard/profile/page.tsx: -------------------------------------------------------------------------------- 1 | import Landing from "@/components/landing"; 2 | import ProfilePage from "@/components/user/profile"; 3 | import UserPosts from "./userposts"; 4 | import { Posts } from "../feed/posts"; 5 | import Reveal from "@/components/reveal"; 6 | 7 | export default async function Profile() { 8 | const posts = await UserPosts(); 9 | return ( 10 |
11 | 12 |
13 |
14 | {posts.map((e, key) => { 15 | return ( 16 | 17 |
18 | 26 |
27 |
28 | ); 29 | })} 30 |
31 |
32 |
33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /actions/register.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import * as z from "zod"; 4 | import { prisma } from "@/lib/db"; 5 | import { RegisterSchema } from "@/schema"; 6 | import bcrypt from "bcrypt"; 7 | import { getUserByEmail } from "@/utils/user"; 8 | import { generateVerificationToken } from "@/lib/token"; 9 | import { sendVerificationEmail } from "@/lib/resend"; 10 | 11 | export const register = async (values: z.infer) => { 12 | const isValid = RegisterSchema.safeParse(values); 13 | console.log("isValid", isValid); 14 | 15 | if (!isValid.success) { 16 | return { error: "Email is not valid" }; 17 | } 18 | 19 | const { email, password, name } = isValid.data; 20 | 21 | const hashedPassword = await bcrypt.hash(password, 10); 22 | 23 | const existingUser = await getUserByEmail(email); 24 | 25 | if (existingUser) { 26 | return { error: "User already exists" }; 27 | } 28 | 29 | await prisma.user.create({ 30 | data: { 31 | email, 32 | password: hashedPassword, 33 | name, 34 | }, 35 | }); 36 | 37 | const verificationToken = await generateVerificationToken(email); 38 | 39 | await sendVerificationEmail(email, verificationToken.token); 40 | 41 | return { success: "Confirmation Email Sent" }; 42 | }; 43 | -------------------------------------------------------------------------------- /app/dashboard/feed/page.tsx: -------------------------------------------------------------------------------- 1 | import { Input } from "@/components/ui/input"; 2 | import { Card } from "@/components/ui/card"; 3 | import PostCard from "@/components/postCard"; 4 | import { useEffect, useState } from "react"; 5 | import axios from "axios"; 6 | import { useRouter } from "next/navigation"; 7 | import z from "zod"; 8 | import { Posts } from "./posts"; 9 | import { GetFeed } from "./getfeed"; 10 | import PostPanel from "@/components/user/postPanel"; 11 | import Reveal from "@/components/reveal"; 12 | 13 | export default async function Feed() { 14 | const feed = await GetFeed(); 15 | return ( 16 |
17 | 18 |
19 | {feed.map((e, key) => { 20 | return ( 21 | 22 |
23 | 31 |
32 |
33 | ); 34 | })} 35 |
36 |
37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /components/postCard.tsx: -------------------------------------------------------------------------------- 1 | import { Card } from "./ui/card"; 2 | 3 | export default function PostCard() { 4 | return ( 5 | 6 |
7 | 12 |
13 |

Politics

14 |

ex-SpaceX

15 |
16 |
17 |

18 | Lorem ipsum dolor sit amet consectetur, adipisicing elit. Quia, quas! 19 |

20 |

21 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Mollitia alias 22 | eos iusto natus ipsum. Cum quasi iure reprehenderit minus quis? 23 |

24 |
25 |
26 | h 27 |
28 |
29 | h 30 |
31 |
32 | h 33 |
34 |
35 |
36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /actions/login.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { LoginSchema } from "@/schema"; 4 | import { z } from "zod"; 5 | import { signIn } from "@/auth"; 6 | import { prisma } from "@/lib/db"; 7 | import { universities } from "./domains"; 8 | 9 | export const login = async (values: z.infer) => { 10 | const confirmationLink = 11 | process.env.NODE_ENV === "development" 12 | ? "http://localhost:3000/confirm-email" 13 | : "https://www.unsight.club/confirm-email"; 14 | const clgEmails = universities; 15 | 16 | // Check to see if data is valid 17 | 18 | const isValid = LoginSchema.safeParse(values); 19 | console.log(isValid); 20 | 21 | if (!isValid.success) { 22 | throw new Error("Email is not valid"); 23 | } 24 | 25 | const { email } = isValid.data; 26 | 27 | if (email) { 28 | for (let i = 0; i < clgEmails.length; i++) { 29 | if (clgEmails[i] == email.split("@")[1]) { 30 | await signIn("resend", { email, redirect: false }); 31 | return { success: `Email sent to ${email}` }; 32 | } 33 | } 34 | throw Error("Invalid Email, use College Email: " + email); 35 | } 36 | // if (email.indexOf("gmail.com") >= 0) { 37 | // throw new Error("Write your clg email"); 38 | // } 39 | 40 | // signIn("email", { email, redirectTo: "/dashboard/feed" });\ 41 | return { Msg: `Provide valid email` }; 42 | }; 43 | -------------------------------------------------------------------------------- /prisma/migrations/20240424202049_init/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - The primary key for the `Comments` table will be changed. If it partially fails, the table could be left without primary key constraint. 5 | - The `id` column on the `Comments` table would be dropped and recreated. This will lead to data loss if there is data in the column. 6 | - The primary key for the `Posts` table will be changed. If it partially fails, the table could be left without primary key constraint. 7 | - The `id` column on the `Posts` table would be dropped and recreated. This will lead to data loss if there is data in the column. 8 | - Added the required column `commentId` to the `Posts` table without a default value. This is not possible if the table is not empty. 9 | 10 | */ 11 | -- DropForeignKey 12 | ALTER TABLE "Posts" DROP CONSTRAINT "Posts_commentId_fkey"; 13 | 14 | -- AlterTable 15 | ALTER TABLE "Comments" DROP CONSTRAINT "Comments_pkey", 16 | DROP COLUMN "id", 17 | ADD COLUMN "id" SERIAL NOT NULL, 18 | ADD CONSTRAINT "Comments_pkey" PRIMARY KEY ("id"); 19 | 20 | -- AlterTable 21 | ALTER TABLE "Posts" DROP CONSTRAINT "Posts_pkey", 22 | DROP COLUMN "id", 23 | ADD COLUMN "id" SERIAL NOT NULL, 24 | DROP COLUMN "commentId", 25 | ADD COLUMN "commentId" INTEGER NOT NULL, 26 | ADD CONSTRAINT "Posts_pkey" PRIMARY KEY ("id"); 27 | 28 | -- AddForeignKey 29 | ALTER TABLE "Posts" ADD CONSTRAINT "Posts_commentId_fkey" FOREIGN KEY ("commentId") REFERENCES "Comments"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 30 | -------------------------------------------------------------------------------- /components/auth/sideSection.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | 3 | export default function SideSection() { 4 | return ( 5 |
6 | 7 |
8 | 13 |

14 | Unsight 15 |

16 |
17 | 18 |
19 |

Welcome to Unsight

20 |

21 | A platform where you can freely share your thoughts all across the 22 | world, without revealing your identity 23 |

24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /components/mainpage/mainpagetitle.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "../ui/button"; 2 | import Reveal from "../reveal"; 3 | import Link from "next/link"; 4 | 5 | export default function MainpageTitle() { 6 | return ( 7 |
8 | 9 |

10 | Building Trust! 11 |

12 |
13 | 14 |

15 | with Anonymity 16 |

17 |
18 | 19 |

20 | Welcome to Unsight, a platform where you can... 21 |

22 |
23 | 24 |

25 | Explore, Connect, and Share With Confidence 26 |

27 |
28 | 29 | 30 | 31 | 34 | 35 | 36 |
37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unsight", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "postinstall": "prisma generate", 9 | "start": "next start", 10 | "lint": "next lint" 11 | }, 12 | "dependencies": { 13 | "@auth/prisma-adapter": "^2.0.0", 14 | "@radix-ui/react-label": "^2.0.2", 15 | "@radix-ui/react-slot": "^1.0.2", 16 | "@radix-ui/react-toast": "^1.1.5", 17 | "@react-email/components": "0.0.16", 18 | "@types/bcrypt": "^5.0.2", 19 | "@types/uuid": "^9.0.8", 20 | "bcrypt": "^5.1.1", 21 | "class-variance-authority": "^0.7.0", 22 | "clsx": "^2.1.0", 23 | "framer-motion": "^11.1.7", 24 | "link-preview-js": "^3.0.5", 25 | "lucide-react": "^0.372.0", 26 | "next": "^14.2.3", 27 | "next-auth": "^5.0.0-beta.16", 28 | "next-themes": "^0.3.0", 29 | "nodemailer": "^6.9.13", 30 | "react": "^18", 31 | "react-dom": "^18", 32 | "react-email": "^1.10.1", 33 | "react-hook-form": "^7.51.3", 34 | "resend": "^3.2.0", 35 | "sonner": "^1.4.41", 36 | "tailwind-merge": "^2.3.0", 37 | "tailwind-scrollbar-hide": "^1.1.7", 38 | "tailwindcss-animate": "^1.0.7", 39 | "uuid": "^9.0.1", 40 | "zod": "^3.22.5" 41 | }, 42 | "devDependencies": { 43 | "@hookform/resolvers": "^3.3.4", 44 | "@prisma/client": "^5.12.1", 45 | "@types/node": "^20", 46 | "@types/react": "^18", 47 | "@types/react-dom": "^18", 48 | "axios": "^1.6.8", 49 | "eslint": "^8", 50 | "eslint-config-next": "14.2.2", 51 | "postcss": "^8", 52 | "prisma": "^5.12.1", 53 | "tailwindcss": "^3.4.1" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /config/site-config.ts: -------------------------------------------------------------------------------- 1 | import { Metadata } from "next"; 2 | 3 | const TITLE = "Unsight - Join the global community of students!"; 4 | const DESCRIPTION = 5 | "Interact in a global community of students. share thoughts, opportunities, resources and more, freely keeping your identity anonymous."; 6 | 7 | const PREVIEW_IMAGE_URL = "https://www.unsight.club/opengraph-image.png"; 8 | const ALT_TITLE = 9 | "Unsight - Join the global community of students with Anonymity"; 10 | const BASE_URL = "https://www.unsight.club"; 11 | 12 | export const siteConfig: Metadata = { 13 | title: TITLE, 14 | description: DESCRIPTION, 15 | icons: { 16 | icon: "/favicon.ico", 17 | }, 18 | applicationName: "Unsight", 19 | creator: "Kartikey", 20 | twitter: { 21 | creator: "@KartikeyStack", 22 | title: TITLE, 23 | description: DESCRIPTION, 24 | card: "summary_large_image", 25 | images: [ 26 | { 27 | url: PREVIEW_IMAGE_URL, 28 | width: 1200, 29 | height: 630, 30 | alt: ALT_TITLE, 31 | }, 32 | ], 33 | }, 34 | openGraph: { 35 | title: TITLE, 36 | description: DESCRIPTION, 37 | siteName: "Unsight", 38 | url: BASE_URL, 39 | locale: "en_US", 40 | type: "website", 41 | images: [ 42 | { 43 | url: PREVIEW_IMAGE_URL, 44 | width: 1200, 45 | height: 630, 46 | alt: ALT_TITLE, 47 | }, 48 | ], 49 | }, 50 | category: "Technology", 51 | alternates: { 52 | canonical: BASE_URL, 53 | }, 54 | keywords: [ 55 | "Unsight", 56 | "Team Blind", 57 | "Club", 58 | "Kartikey Verma app", 59 | "Community", 60 | "Anonymous", 61 | "Anonymity", 62 | "Share your thoughts freely", 63 | "Students community", 64 | ], 65 | metadataBase: new URL(BASE_URL), 66 | }; 67 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | body { 7 | background-color: black; 8 | } 9 | :root { 10 | --background: 0 0% 100%; 11 | --foreground: 222.2 84% 4.9%; 12 | scroll-behavior: smooth; 13 | 14 | --card: 0 0% 100%; 15 | --card-foreground: 222.2 84% 4.9%; 16 | 17 | --popover: 0 0% 100%; 18 | --popover-foreground: 222.2 84% 4.9%; 19 | 20 | --primary: 222.2 47.4% 11.2%; 21 | --primary-foreground: 210 40% 98%; 22 | 23 | --secondary: 210 40% 96.1%; 24 | --secondary-foreground: 222.2 47.4% 11.2%; 25 | 26 | --muted: 210 40% 96.1%; 27 | --muted-foreground: 215.4 16.3% 46.9%; 28 | 29 | --accent: 210 40% 96.1%; 30 | --accent-foreground: 222.2 47.4% 11.2%; 31 | 32 | --destructive: 0 84.2% 60.2%; 33 | --destructive-foreground: 210 40% 98%; 34 | 35 | --border: 214.3 31.8% 91.4%; 36 | --input: 214.3 31.8% 91.4%; 37 | --ring: 222.2 84% 4.9%; 38 | 39 | --radius: 0.5rem; 40 | } 41 | 42 | .dark { 43 | scroll-behavior: smooth; 44 | --background: 222.2 84% 4.9%; 45 | --foreground: 210 40% 98%; 46 | 47 | --card: 222.2 84% 4.9%; 48 | --card-foreground: 210 40% 98%; 49 | 50 | --popover: 222.2 84% 4.9%; 51 | --popover-foreground: 210 40% 98%; 52 | 53 | --primary: 210 40% 98%; 54 | --primary-foreground: 222.2 47.4% 11.2%; 55 | 56 | --secondary: 217.2 32.6% 17.5%; 57 | --secondary-foreground: 210 40% 98%; 58 | 59 | --muted: 217.2 32.6% 17.5%; 60 | --muted-foreground: 215 20.2% 65.1%; 61 | 62 | --accent: 217.2 32.6% 17.5%; 63 | --accent-foreground: 210 40% 98%; 64 | 65 | --destructive: 0 62.8% 30.6%; 66 | --destructive-foreground: 210 40% 98%; 67 | 68 | --border: 217.2 32.6% 17.5%; 69 | --input: 217.2 32.6% 17.5%; 70 | --ring: 212.7 26.8% 83.9%; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /components/explorebar.tsx: -------------------------------------------------------------------------------- 1 | import { Card } from "./ui/card"; 2 | 3 | export default function Explorebar() { 4 | return ( 5 |
6 | 7 | sponsored 8 | 9 | 10 |

Popular posts

11 |
12 |
13 |
14 |

Design Career

15 | 16 |

145

17 |
18 |

19 | UX Design and Product Design Job Market 20 |

21 |
22 |
23 |
24 |
25 |
26 |

Web3 Career

27 | 28 |

8k

29 |
30 |

Solana or Ethereum Ecosystem

31 |
32 |
33 |
34 |
35 |
36 |

Personal Finance

37 | 38 |

1145

39 |
40 |

How to do car insurance

41 |
42 |
43 |
44 |
45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Slot } from "@radix-ui/react-slot" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", 9 | { 10 | variants: { 11 | variant: { 12 | default: "bg-primary text-primary-foreground hover:bg-primary/90", 13 | destructive: 14 | "bg-destructive text-destructive-foreground hover:bg-destructive/90", 15 | outline: 16 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground", 17 | secondary: 18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80", 19 | ghost: "hover:bg-accent hover:text-accent-foreground", 20 | link: "text-primary underline-offset-4 hover:underline", 21 | }, 22 | size: { 23 | default: "h-10 px-4 py-2", 24 | sm: "h-9 rounded-md px-3", 25 | lg: "h-11 rounded-md px-8", 26 | icon: "h-10 w-10", 27 | }, 28 | }, 29 | defaultVariants: { 30 | variant: "default", 31 | size: "default", 32 | }, 33 | } 34 | ) 35 | 36 | export interface ButtonProps 37 | extends React.ButtonHTMLAttributes, 38 | VariantProps { 39 | asChild?: boolean 40 | } 41 | 42 | const Button = React.forwardRef( 43 | ({ className, variant, size, asChild = false, ...props }, ref) => { 44 | const Comp = asChild ? Slot : "button" 45 | return ( 46 | 51 | ) 52 | } 53 | ) 54 | Button.displayName = "Button" 55 | 56 | export { Button, buttonVariants } 57 | -------------------------------------------------------------------------------- /components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Card = React.forwardRef< 6 | HTMLDivElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
17 | )) 18 | Card.displayName = "Card" 19 | 20 | const CardHeader = React.forwardRef< 21 | HTMLDivElement, 22 | React.HTMLAttributes 23 | >(({ className, ...props }, ref) => ( 24 |
29 | )) 30 | CardHeader.displayName = "CardHeader" 31 | 32 | const CardTitle = React.forwardRef< 33 | HTMLParagraphElement, 34 | React.HTMLAttributes 35 | >(({ className, ...props }, ref) => ( 36 |

44 | )) 45 | CardTitle.displayName = "CardTitle" 46 | 47 | const CardDescription = React.forwardRef< 48 | HTMLParagraphElement, 49 | React.HTMLAttributes 50 | >(({ className, ...props }, ref) => ( 51 |

56 | )) 57 | CardDescription.displayName = "CardDescription" 58 | 59 | const CardContent = React.forwardRef< 60 | HTMLDivElement, 61 | React.HTMLAttributes 62 | >(({ className, ...props }, ref) => ( 63 |

64 | )) 65 | CardContent.displayName = "CardContent" 66 | 67 | const CardFooter = React.forwardRef< 68 | HTMLDivElement, 69 | React.HTMLAttributes 70 | >(({ className, ...props }, ref) => ( 71 |
76 | )) 77 | CardFooter.displayName = "CardFooter" 78 | 79 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } 80 | -------------------------------------------------------------------------------- /components/mainpage/imagebanner.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Image from "next/image"; 3 | import bg2 from "@/public/bg2.png"; 4 | import bg1 from "@/public/bg1.png"; 5 | import bg3 from "@/public/bg3.png"; 6 | import { useEffect, useState, useMemo } from "react"; 7 | 8 | export default function ImageBanner() { 9 | const [bannerImg, setBannerImg] = useState(bg1); 10 | const [index, setIndex] = useState(0); 11 | // const images = [bg1, bg2, bg3]; 12 | const images = useMemo(() => [bg1, bg2, bg3], []); 13 | 14 | useEffect(() => { 15 | const intervalId = setInterval(() => { 16 | setIndex((prevIndex) => (prevIndex + 1) % images.length); 17 | }, 5000); 18 | 19 | return () => clearInterval(intervalId); 20 | }, [images]); 21 | 22 | useEffect(() => { 23 | setBannerImg(images[index]); 24 | }, [index, images]); 25 | return ( 26 |
27 |
28 | img 29 |
30 |
31 | 39 | 47 | 55 |
56 |
57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config = { 4 | darkMode: ["class"], 5 | content: [ 6 | "./pages/**/*.{ts,tsx}", 7 | "./components/**/*.{ts,tsx}", 8 | "./app/**/*.{ts,tsx}", 9 | "./src/**/*.{ts,tsx}", 10 | ], 11 | prefix: "", 12 | theme: { 13 | container: { 14 | center: true, 15 | padding: "2rem", 16 | screens: { 17 | "2xl": "1400px", 18 | }, 19 | }, 20 | extend: { 21 | colors: { 22 | border: "hsl(var(--border))", 23 | input: "hsl(var(--input))", 24 | ring: "hsl(var(--ring))", 25 | background: "hsl(var(--background))", 26 | foreground: "hsl(var(--foreground))", 27 | primary: { 28 | DEFAULT: "hsl(var(--primary))", 29 | foreground: "hsl(var(--primary-foreground))", 30 | }, 31 | secondary: { 32 | DEFAULT: "hsl(var(--secondary))", 33 | foreground: "hsl(var(--secondary-foreground))", 34 | }, 35 | destructive: { 36 | DEFAULT: "hsl(var(--destructive))", 37 | foreground: "hsl(var(--destructive-foreground))", 38 | }, 39 | muted: { 40 | DEFAULT: "hsl(var(--muted))", 41 | foreground: "hsl(var(--muted-foreground))", 42 | }, 43 | accent: { 44 | DEFAULT: "hsl(var(--accent))", 45 | foreground: "hsl(var(--accent-foreground))", 46 | }, 47 | popover: { 48 | DEFAULT: "hsl(var(--popover))", 49 | foreground: "hsl(var(--popover-foreground))", 50 | }, 51 | card: { 52 | DEFAULT: "hsl(var(--card))", 53 | foreground: "hsl(var(--card-foreground))", 54 | }, 55 | }, 56 | borderRadius: { 57 | lg: "var(--radius)", 58 | md: "calc(var(--radius) - 2px)", 59 | sm: "calc(var(--radius) - 4px)", 60 | }, 61 | keyframes: { 62 | "accordion-down": { 63 | from: { height: "0" }, 64 | to: { height: "var(--radix-accordion-content-height)" }, 65 | }, 66 | "accordion-up": { 67 | from: { height: "var(--radix-accordion-content-height)" }, 68 | to: { height: "0" }, 69 | }, 70 | }, 71 | animation: { 72 | "accordion-down": "accordion-down 0.2s ease-out", 73 | "accordion-up": "accordion-up 0.2s ease-out", 74 | }, 75 | }, 76 | }, 77 | plugins: [require("tailwindcss-animate"), require("tailwind-scrollbar-hide")], 78 | } satisfies Config; 79 | 80 | export default config; 81 | -------------------------------------------------------------------------------- /prisma/migrations/20240421200423_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "User" ( 3 | "id" TEXT NOT NULL, 4 | "name" TEXT, 5 | "email" TEXT NOT NULL, 6 | "emailVerified" TIMESTAMP(3), 7 | "image" TEXT, 8 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 9 | "updatedAt" TIMESTAMP(3) NOT NULL, 10 | 11 | CONSTRAINT "User_pkey" PRIMARY KEY ("id") 12 | ); 13 | 14 | -- CreateTable 15 | CREATE TABLE "Account" ( 16 | "userId" TEXT NOT NULL, 17 | "type" TEXT NOT NULL, 18 | "provider" TEXT NOT NULL, 19 | "providerAccountId" TEXT NOT NULL, 20 | "refresh_token" TEXT, 21 | "access_token" TEXT, 22 | "expires_at" INTEGER, 23 | "token_type" TEXT, 24 | "scope" TEXT, 25 | "id_token" TEXT, 26 | "session_state" TEXT, 27 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 28 | "updatedAt" TIMESTAMP(3) NOT NULL, 29 | 30 | CONSTRAINT "Account_pkey" PRIMARY KEY ("provider","providerAccountId") 31 | ); 32 | 33 | -- CreateTable 34 | CREATE TABLE "Session" ( 35 | "sessionToken" TEXT NOT NULL, 36 | "userId" TEXT NOT NULL, 37 | "expires" TIMESTAMP(3) NOT NULL, 38 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 39 | "updatedAt" TIMESTAMP(3) NOT NULL 40 | ); 41 | 42 | -- CreateTable 43 | CREATE TABLE "VerificationToken" ( 44 | "identifier" TEXT NOT NULL, 45 | "token" TEXT NOT NULL, 46 | "expires" TIMESTAMP(3) NOT NULL, 47 | "email" TEXT, 48 | 49 | CONSTRAINT "VerificationToken_pkey" PRIMARY KEY ("identifier","token") 50 | ); 51 | 52 | -- CreateTable 53 | CREATE TABLE "Authenticator" ( 54 | "id" TEXT NOT NULL, 55 | "credentialID" TEXT NOT NULL, 56 | "userId" TEXT NOT NULL, 57 | "providerAccountId" TEXT NOT NULL, 58 | "credentialPublicKey" TEXT NOT NULL, 59 | "counter" INTEGER NOT NULL, 60 | "credentialDeviceType" TEXT NOT NULL, 61 | "credentialBackedUp" BOOLEAN NOT NULL, 62 | "transports" TEXT, 63 | 64 | CONSTRAINT "Authenticator_pkey" PRIMARY KEY ("id") 65 | ); 66 | 67 | -- CreateIndex 68 | CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); 69 | 70 | -- CreateIndex 71 | CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken"); 72 | 73 | -- CreateIndex 74 | CREATE UNIQUE INDEX "Authenticator_credentialID_key" ON "Authenticator"("credentialID"); 75 | 76 | -- AddForeignKey 77 | ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 78 | 79 | -- AddForeignKey 80 | ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 81 | 82 | -- AddForeignKey 83 | ALTER TABLE "Authenticator" ADD CONSTRAINT "Authenticator_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 84 | -------------------------------------------------------------------------------- /components/user/profile.tsx: -------------------------------------------------------------------------------- 1 | import Reveal from "../reveal"; 2 | 3 | export default function ProfilePage({ 4 | profileImg, 5 | name, 6 | bannerImg, 7 | id, 8 | }: { 9 | profileImg: string; 10 | name: string; 11 | bannerImg: string; 12 | id: string; 13 | }) { 14 | return ( 15 |
16 |
17 |
18 | 23 |
24 |
25 | 26 |
27 | 32 |
33 |
34 | 35 |
Kartikey Verma
36 |
37 | 38 |
39 | 44 | 48 | 49 |

Public

50 |
51 |
52 |
53 |
54 | 55 |
56 | Follow 57 |
58 |
59 |
60 |
61 |
62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /components/landing.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Input } from "./ui/input"; 4 | import { Card } from "./ui/card"; 5 | import PostCard from "./postCard"; 6 | import { useState } from "react"; 7 | import axios from "axios"; 8 | import { useRouter } from "next/navigation"; 9 | export default function Landing() { 10 | const router = useRouter(); 11 | const [description, setPost] = useState(""); 12 | 13 | const handleOnclick = async () => { 14 | try { 15 | const apiUrl = 16 | process.env.NODE_ENV === "development" 17 | ? "http://localhost:3000/api/user/createpost" 18 | : "https://www.unsight.club/api/user/createpost"; 19 | const response = await axios.post(apiUrl, { description }); 20 | console.log("el"); 21 | 22 | router.push("/"); 23 | return response.data; 24 | } catch (error) { 25 | return `Error: ${error}`; 26 | } 27 | }; 28 | 29 | return ( 30 |
31 |
32 |

33 | Unsight - Anonymous and Professional Community of Students 34 |

35 |
36 | { 38 | setPost(e.target.value); 39 | }} 40 | className="h-16 text-xl bg-slate-300 rounded-xl w-[100%]" 41 | placeholder="Start a post..." 42 | /> 43 | 67 |
68 |
69 |
70 | 71 | 72 | 73 | 74 |
75 |
76 | ); 77 | } 78 | -------------------------------------------------------------------------------- /app/resources/resourcecard.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Button } from "@/components/ui/button"; 3 | import { useEffect, useRef } from "react"; 4 | 5 | export default function ResourceCard({ 6 | id, 7 | userId, 8 | categary, 9 | title, 10 | description, 11 | resourceLink, 12 | }: { 13 | id: number; 14 | userId: string; 15 | categary: string; 16 | title: string; 17 | description: string; 18 | resourceLink: string; 19 | }) { 20 | const cardRef = useRef(null); 21 | 22 | useEffect(() => { 23 | if (typeof window !== "undefined") { 24 | const card = cardRef.current; 25 | 26 | const handleMouseMove = (e: MouseEvent) => { 27 | if (card) { 28 | const rect = card.getBoundingClientRect(); 29 | const x = e.clientX - rect.left; 30 | const y = e.clientY - rect.top; 31 | card.style.setProperty("--x", `${x}px`); 32 | card.style.setProperty("--y", `${y}px`); 33 | } 34 | }; 35 | 36 | card?.addEventListener("mousemove", handleMouseMove); 37 | 38 | return () => { 39 | card?.removeEventListener("mousemove", handleMouseMove); 40 | }; 41 | } 42 | }, []); 43 | 44 | return ( 45 |
49 |
50 |

{title}

51 |

{description}

52 |
53 |
54 | {categary} 55 |
56 |
57 | 60 | 70 |
71 | 72 | 99 |
100 | ); 101 | } 102 | -------------------------------------------------------------------------------- /prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | // Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? 5 | // Try Prisma Accelerate: https://pris.ly/cli/accelerate-init 6 | 7 | generator client { 8 | provider = "prisma-client-js" 9 | } 10 | 11 | datasource db { 12 | provider = "postgresql" 13 | url = env("DATABASE_URL") 14 | } 15 | 16 | model User { 17 | id String @id @default(cuid()) 18 | name String? 19 | email String @unique 20 | password String? 21 | emailVerified DateTime? 22 | image String? 23 | accounts Account[] 24 | sessions Session[] 25 | posts Posts[] 26 | // Optional for WebAuthn support 27 | Authenticator Authenticator[] 28 | 29 | createdAt DateTime @default(now()) 30 | updatedAt DateTime @updatedAt 31 | Resources Resources[] 32 | } 33 | 34 | model Posts { 35 | id Int @id @default(autoincrement()) 36 | description String 37 | imgUrl String? 38 | user User @relation(fields: [userId], references: [id]) 39 | userId String 40 | like Int? @default(0) 41 | } 42 | 43 | model Resources { 44 | id Int @id @default(autoincrement()) 45 | title String 46 | description String 47 | user User @relation(fields: [userId], references: [id]) 48 | userId String 49 | categary String 50 | resourceLink String 51 | } 52 | 53 | model Account { 54 | userId String 55 | type String 56 | provider String 57 | providerAccountId String 58 | refresh_token String? 59 | access_token String? 60 | expires_at Int? 61 | token_type String? 62 | scope String? 63 | id_token String? 64 | session_state String? 65 | 66 | createdAt DateTime @default(now()) 67 | updatedAt DateTime @updatedAt 68 | 69 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 70 | 71 | @@id([provider, providerAccountId]) 72 | } 73 | 74 | model Session { 75 | sessionToken String @unique 76 | userId String 77 | expires DateTime 78 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 79 | 80 | createdAt DateTime @default(now()) 81 | updatedAt DateTime @updatedAt 82 | } 83 | 84 | model VerificationToken { 85 | identifier String 86 | token String @unique 87 | expires DateTime 88 | email String? 89 | 90 | @@id([identifier, token]) 91 | } 92 | 93 | // Optional for WebAuthn support 94 | model Authenticator { 95 | id String @id @default(cuid()) 96 | credentialID String @unique 97 | userId String 98 | providerAccountId String 99 | credentialPublicKey String 100 | counter Int 101 | credentialDeviceType String 102 | credentialBackedUp Boolean 103 | transports String? 104 | 105 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 106 | } 107 | -------------------------------------------------------------------------------- /app/resources/add/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Button } from "@/components/ui/button"; 4 | import { useState } from "react"; 5 | import { useSession } from "next-auth/react"; 6 | import { useRouter } from "next/navigation"; 7 | import axios from "axios"; 8 | 9 | export default function Add() { 10 | const [title, setTitle] = useState(""); 11 | const [description, setDescription] = useState(""); 12 | const [categary, setCategary] = useState(""); 13 | const [resourceLink, setResourceLink] = useState(""); 14 | 15 | const session = useSession(); 16 | const router = useRouter(); 17 | 18 | const handleOnClick = async () => { 19 | try { 20 | const apiUrl = 21 | process.env.NODE_ENV === "development" 22 | ? "http://localhost:3000/api/user/addresourse" 23 | : "https://www.unsight.club/api/user/addresourse"; 24 | const res = await axios.post(apiUrl, { 25 | title: title, 26 | description: description, 27 | categary: categary, 28 | resourceLink: resourceLink, 29 | }); 30 | 31 | router.push("/resources/all"); 32 | return res.data; 33 | } catch (error) { 34 | console.log(error); 35 | 36 | return `Error: ${error}`; 37 | } 38 | }; 39 | 40 | return ( 41 |
42 |
43 |

44 | Add Resources 45 |

46 | { 51 | setTitle(e.target.value); 52 | }} 53 | /> 54 | 55 | 69 | 70 | { 75 | setResourceLink(e.target.value); 76 | }} 77 | /> 78 | 89 | 92 |
93 |
94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /components/auth/signinWithEmail.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as z from "zod"; 4 | import { useForm } from "react-hook-form"; 5 | import { useState } from "react"; 6 | import { zodResolver } from "@hookform/resolvers/zod"; 7 | import { 8 | Form, 9 | FormControl, 10 | FormField, 11 | FormItem, 12 | FormLabel, 13 | FormMessage, 14 | } from "@/components/ui/form"; 15 | import { Card, CardContent, CardHeader } from "@/components/ui/card"; 16 | import { LoginSchema } from "@/schema"; 17 | import { Input } from "@/components/ui/input"; 18 | import { Button } from "@/components/ui/button"; 19 | import { login } from "@/actions/login"; 20 | import Link from "next/link"; 21 | import SideSection from "./sideSection"; 22 | 23 | export default function SignInForm() { 24 | const [success, setSuccess] = useState(""); 25 | 26 | // Define your form 27 | const form = useForm>({ 28 | resolver: zodResolver(LoginSchema), 29 | defaultValues: { 30 | email: "", 31 | }, 32 | }); 33 | 34 | const onSubmit = async (values: z.infer) => { 35 | login(values).then((data) => { 36 | if (data.success) { 37 | setSuccess(data.success); 38 | } 39 | }); 40 | }; 41 | 42 | return ( 43 |
44 | 45 |
46 | 47 | 48 |

49 | Email Provider Authentication 50 |

51 |
52 | 53 |
54 | 58 | ( 62 | 63 | Sign in with your student email id 64 | 65 | 70 | 71 | 72 | 73 | )} 74 | /> 75 | {success && ( 76 |

77 | {success} 78 |

79 | )} 80 | 86 | 87 | 88 | 89 |

90 | Create an account? 91 |

92 | 93 |
94 |
95 |
96 |
97 | ); 98 | } 99 | -------------------------------------------------------------------------------- /components/user/postPanel.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useRouter } from "next/navigation"; 3 | import { Input } from "../ui/input"; 4 | import { useState } from "react"; 5 | import axios from "axios"; 6 | import { throttle } from "./throttle"; 7 | 8 | import { z } from "zod"; 9 | 10 | export default function PostPanel() { 11 | const router = useRouter(); 12 | const [description, setPost] = useState(""); 13 | const [isAdding, setIsAdding] = useState(false); 14 | 15 | const handleOnclick = async () => { 16 | try { 17 | setIsAdding(true); 18 | const apiUrl = 19 | process.env.NODE_ENV === "development" 20 | ? "http://localhost:3000/api/user/createpost" 21 | : "https://www.unsight.club/api/user/createpost"; 22 | const response = await axios.post(apiUrl, { description }); 23 | setPost(""); 24 | window.location.reload(); 25 | return response.data; 26 | } finally { 27 | setIsAdding(false); 28 | } 29 | }; 30 | 31 | const throttledAdd = throttle(handleOnclick, 2000); 32 | 33 | return ( 34 |
35 | {/*

36 | Unsight - Anonymous and Professional Community of Students 37 |

*/} 38 |
39 | { 41 | setPost(e.target.value); 42 | }} 43 | className="h-16 text-xl bg-black rounded-xl w-[100%]" 44 | placeholder="Start a post..." 45 | /> 46 | 88 |
89 |
90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /components/mainpage/intro.tsx: -------------------------------------------------------------------------------- 1 | import { Card } from "../ui/card"; 2 | import FeatureCard from "./featureCard"; 3 | 4 | export default function IntroPage() { 5 | return ( 6 |
7 |

8 | Verified Voices, Anonymous Conversations, Endless Insights. 9 |

10 |
11 | 15 | 19 | 20 | } 21 | description="Interact with the global student community, share your thoughts without hesitation" 22 | /> 23 | 27 | 28 | 29 | 30 | 31 | 32 | } 33 | description="Help in peer learning by sharing resources & utilize huge number of resources tried by others " 34 | /> 35 | 39 | 43 | 44 | } 45 | description="Anonymously share/see reviews" 46 | /> 47 | 51 | 58 | 59 | 60 | 61 | 62 | } 63 | description="Build your network with other students around the world, share opportunities" 64 | /> 65 |
66 |
67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /components/auth/registerWithCred.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as z from "zod"; 4 | import { useForm } from "react-hook-form"; 5 | import { useState } from "react"; 6 | import { zodResolver } from "@hookform/resolvers/zod"; 7 | import Link from "next/link"; 8 | import { 9 | Form, 10 | FormControl, 11 | FormField, 12 | FormItem, 13 | FormLabel, 14 | FormMessage, 15 | } from "@/components/ui/form"; 16 | import { Card, CardContent, CardHeader } from "@/components/ui/card"; 17 | import { RegisterSchema } from "@/schema"; 18 | import { Input } from "@/components/ui/input"; 19 | import { Button } from "@/components/ui/button"; 20 | import { login } from "@/actions/login"; 21 | import { register } from "@/actions/register"; 22 | import SideSection from "./sideSection"; 23 | 24 | const RegisterCredentialsForm = () => { 25 | const [success, setSuccess] = useState(""); 26 | 27 | // Define your form 28 | const form = useForm>({ 29 | resolver: zodResolver(RegisterSchema), 30 | defaultValues: { 31 | email: "", 32 | }, 33 | }); 34 | 35 | const onSubmit = async (values: z.infer) => { 36 | register(values).then((data) => { 37 | // setSuccess(data?.success); 38 | console.log(data); 39 | }); 40 | }; 41 | 42 | return ( 43 |
44 | 45 |
46 | 47 | 48 |

Register With Credentials

49 |
50 | 51 |
52 | 56 | ( 60 | 61 | Name 62 | 63 | 64 | 65 | 66 | 67 | )} 68 | /> 69 | ( 73 | 74 | Email 75 | 76 | 81 | 82 | 83 | 84 | )} 85 | /> 86 | ( 90 | 91 | Password 92 | 93 | 98 | 99 | 100 | 101 | )} 102 | /> 103 | {success && ( 104 |

105 | {success} 106 |

107 | )} 108 | 111 | 112 | 113 | 114 |

115 | Login 116 |

117 | 118 |
119 |
120 |
121 |
122 | ); 123 | }; 124 | 125 | export default RegisterCredentialsForm; 126 | -------------------------------------------------------------------------------- /components/ui/use-toast.ts: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | // Inspired by react-hot-toast library 4 | import * as React from "react" 5 | 6 | import type { 7 | ToastActionElement, 8 | ToastProps, 9 | } from "@/components/ui/toast" 10 | 11 | const TOAST_LIMIT = 1 12 | const TOAST_REMOVE_DELAY = 1000000 13 | 14 | type ToasterToast = ToastProps & { 15 | id: string 16 | title?: React.ReactNode 17 | description?: React.ReactNode 18 | action?: ToastActionElement 19 | } 20 | 21 | const actionTypes = { 22 | ADD_TOAST: "ADD_TOAST", 23 | UPDATE_TOAST: "UPDATE_TOAST", 24 | DISMISS_TOAST: "DISMISS_TOAST", 25 | REMOVE_TOAST: "REMOVE_TOAST", 26 | } as const 27 | 28 | let count = 0 29 | 30 | function genId() { 31 | count = (count + 1) % Number.MAX_SAFE_INTEGER 32 | return count.toString() 33 | } 34 | 35 | type ActionType = typeof actionTypes 36 | 37 | type Action = 38 | | { 39 | type: ActionType["ADD_TOAST"] 40 | toast: ToasterToast 41 | } 42 | | { 43 | type: ActionType["UPDATE_TOAST"] 44 | toast: Partial 45 | } 46 | | { 47 | type: ActionType["DISMISS_TOAST"] 48 | toastId?: ToasterToast["id"] 49 | } 50 | | { 51 | type: ActionType["REMOVE_TOAST"] 52 | toastId?: ToasterToast["id"] 53 | } 54 | 55 | interface State { 56 | toasts: ToasterToast[] 57 | } 58 | 59 | const toastTimeouts = new Map>() 60 | 61 | const addToRemoveQueue = (toastId: string) => { 62 | if (toastTimeouts.has(toastId)) { 63 | return 64 | } 65 | 66 | const timeout = setTimeout(() => { 67 | toastTimeouts.delete(toastId) 68 | dispatch({ 69 | type: "REMOVE_TOAST", 70 | toastId: toastId, 71 | }) 72 | }, TOAST_REMOVE_DELAY) 73 | 74 | toastTimeouts.set(toastId, timeout) 75 | } 76 | 77 | export const reducer = (state: State, action: Action): State => { 78 | switch (action.type) { 79 | case "ADD_TOAST": 80 | return { 81 | ...state, 82 | toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), 83 | } 84 | 85 | case "UPDATE_TOAST": 86 | return { 87 | ...state, 88 | toasts: state.toasts.map((t) => 89 | t.id === action.toast.id ? { ...t, ...action.toast } : t 90 | ), 91 | } 92 | 93 | case "DISMISS_TOAST": { 94 | const { toastId } = action 95 | 96 | // ! Side effects ! - This could be extracted into a dismissToast() action, 97 | // but I'll keep it here for simplicity 98 | if (toastId) { 99 | addToRemoveQueue(toastId) 100 | } else { 101 | state.toasts.forEach((toast) => { 102 | addToRemoveQueue(toast.id) 103 | }) 104 | } 105 | 106 | return { 107 | ...state, 108 | toasts: state.toasts.map((t) => 109 | t.id === toastId || toastId === undefined 110 | ? { 111 | ...t, 112 | open: false, 113 | } 114 | : t 115 | ), 116 | } 117 | } 118 | case "REMOVE_TOAST": 119 | if (action.toastId === undefined) { 120 | return { 121 | ...state, 122 | toasts: [], 123 | } 124 | } 125 | return { 126 | ...state, 127 | toasts: state.toasts.filter((t) => t.id !== action.toastId), 128 | } 129 | } 130 | } 131 | 132 | const listeners: Array<(state: State) => void> = [] 133 | 134 | let memoryState: State = { toasts: [] } 135 | 136 | function dispatch(action: Action) { 137 | memoryState = reducer(memoryState, action) 138 | listeners.forEach((listener) => { 139 | listener(memoryState) 140 | }) 141 | } 142 | 143 | type Toast = Omit 144 | 145 | function toast({ ...props }: Toast) { 146 | const id = genId() 147 | 148 | const update = (props: ToasterToast) => 149 | dispatch({ 150 | type: "UPDATE_TOAST", 151 | toast: { ...props, id }, 152 | }) 153 | const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }) 154 | 155 | dispatch({ 156 | type: "ADD_TOAST", 157 | toast: { 158 | ...props, 159 | id, 160 | open: true, 161 | onOpenChange: (open) => { 162 | if (!open) dismiss() 163 | }, 164 | }, 165 | }) 166 | 167 | return { 168 | id: id, 169 | dismiss, 170 | update, 171 | } 172 | } 173 | 174 | function useToast() { 175 | const [state, setState] = React.useState(memoryState) 176 | 177 | React.useEffect(() => { 178 | listeners.push(setState) 179 | return () => { 180 | const index = listeners.indexOf(setState) 181 | if (index > -1) { 182 | listeners.splice(index, 1) 183 | } 184 | } 185 | }, [state]) 186 | 187 | return { 188 | ...state, 189 | toast, 190 | dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }), 191 | } 192 | } 193 | 194 | export { useToast, toast } 195 | -------------------------------------------------------------------------------- /components/ui/form.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as LabelPrimitive from "@radix-ui/react-label" 3 | import { Slot } from "@radix-ui/react-slot" 4 | import { 5 | Controller, 6 | ControllerProps, 7 | FieldPath, 8 | FieldValues, 9 | FormProvider, 10 | useFormContext, 11 | } from "react-hook-form" 12 | 13 | import { cn } from "@/lib/utils" 14 | import { Label } from "@/components/ui/label" 15 | 16 | const Form = FormProvider 17 | 18 | type FormFieldContextValue< 19 | TFieldValues extends FieldValues = FieldValues, 20 | TName extends FieldPath = FieldPath 21 | > = { 22 | name: TName 23 | } 24 | 25 | const FormFieldContext = React.createContext( 26 | {} as FormFieldContextValue 27 | ) 28 | 29 | const FormField = < 30 | TFieldValues extends FieldValues = FieldValues, 31 | TName extends FieldPath = FieldPath 32 | >({ 33 | ...props 34 | }: ControllerProps) => { 35 | return ( 36 | 37 | 38 | 39 | ) 40 | } 41 | 42 | const useFormField = () => { 43 | const fieldContext = React.useContext(FormFieldContext) 44 | const itemContext = React.useContext(FormItemContext) 45 | const { getFieldState, formState } = useFormContext() 46 | 47 | const fieldState = getFieldState(fieldContext.name, formState) 48 | 49 | if (!fieldContext) { 50 | throw new Error("useFormField should be used within ") 51 | } 52 | 53 | const { id } = itemContext 54 | 55 | return { 56 | id, 57 | name: fieldContext.name, 58 | formItemId: `${id}-form-item`, 59 | formDescriptionId: `${id}-form-item-description`, 60 | formMessageId: `${id}-form-item-message`, 61 | ...fieldState, 62 | } 63 | } 64 | 65 | type FormItemContextValue = { 66 | id: string 67 | } 68 | 69 | const FormItemContext = React.createContext( 70 | {} as FormItemContextValue 71 | ) 72 | 73 | const FormItem = React.forwardRef< 74 | HTMLDivElement, 75 | React.HTMLAttributes 76 | >(({ className, ...props }, ref) => { 77 | const id = React.useId() 78 | 79 | return ( 80 | 81 |
82 | 83 | ) 84 | }) 85 | FormItem.displayName = "FormItem" 86 | 87 | const FormLabel = React.forwardRef< 88 | React.ElementRef, 89 | React.ComponentPropsWithoutRef 90 | >(({ className, ...props }, ref) => { 91 | const { error, formItemId } = useFormField() 92 | 93 | return ( 94 |