├── components ├── Boxes.tsx ├── ProjectCard.tsx ├── SearchFormReset.tsx ├── ui │ ├── textarea.tsx │ ├── input.tsx │ ├── toaster.tsx │ ├── button.tsx │ ├── background-boxes.tsx │ ├── evervault-card.tsx │ ├── 3d-card.tsx │ └── toast.tsx ├── SearchForm.tsx ├── UserProjects.tsx ├── Views.tsx ├── NavBar.tsx ├── ThreeDCard.tsx └── ProjectForm.tsx ├── thumb.png ├── app ├── favicon.ico ├── api │ ├── auth │ │ └── [...nextauth] │ │ │ └── route.ts │ └── sentry-example-api │ │ └── route.ts ├── (root) │ ├── layout.tsx │ ├── project │ │ ├── create │ │ │ └── page.tsx │ │ └── [id] │ │ │ └── page.tsx │ ├── page.tsx │ └── user │ │ └── [id] │ │ └── page.tsx ├── studio │ └── [[...tool]] │ │ └── page.tsx ├── global-error.tsx ├── layout.tsx ├── sentry-example-page │ └── page.tsx └── globals.css ├── public ├── vercel.svg ├── window.svg ├── file.svg ├── globe.svg └── next.svg ├── postcss.config.mjs ├── sanity-typegen.json ├── sanity ├── schemaTypes │ ├── index.ts │ ├── playlist.ts │ ├── author.ts │ └── project.ts ├── lib │ ├── client.ts │ ├── write-client.ts │ ├── image.ts │ ├── live.ts │ └── queries.ts ├── structure.ts ├── env.ts └── extract.json ├── instrumentation.ts ├── sanity.cli.ts ├── eslint.config.mjs ├── components.json ├── lib ├── utils.ts ├── validation.ts └── action.ts ├── sentry.server.config.ts ├── tsconfig.json ├── .gitignore ├── sentry.edge.config.ts ├── sanity.config.ts ├── auth.ts ├── sentry.client.config.ts ├── package.json ├── next.config.ts ├── tailwind.config.ts ├── README.md ├── hooks └── use-toast.ts ├── sanity.types.ts ├── sentry-wizard-installation-error-1734501827302.log └── sentry-wizard-installation-error-1734501928615.log /components/Boxes.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhruvarne22/ProjectHubYT/HEAD/thumb.png -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhruvarne22/ProjectHubYT/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | import { handlers } from "@/auth" // Referring to the auth.ts we just created 2 | export const { GET, POST } = handlers -------------------------------------------------------------------------------- /components/ProjectCard.tsx: -------------------------------------------------------------------------------- 1 | import { Project, Author } from "@/sanity.types"; 2 | 3 | export type ProjectTypeCard = Omit & {author ? : Author}; -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /sanity-typegen.json: -------------------------------------------------------------------------------- 1 | { 2 | "path": "./src/**/*.{ts,tsx,js,jsx}", 3 | "schema": "./sanity/extract.json", 4 | "generates": "./sanity.types.ts", 5 | "overloadClientMethods": true 6 | } -------------------------------------------------------------------------------- /app/(root)/layout.tsx: -------------------------------------------------------------------------------- 1 | import NavBar from "@/components/NavBar"; 2 | 3 | export default function Layout({children} : Readonly<{children:React.ReactNode}>){ 4 | return ( 5 |
6 | 7 | {children} 8 |
9 | ) 10 | } -------------------------------------------------------------------------------- /sanity/schemaTypes/index.ts: -------------------------------------------------------------------------------- 1 | import { type SchemaTypeDefinition } from 'sanity' 2 | import { author } from './author' 3 | import { project } from './project' 4 | import { playlist } from './playlist' 5 | 6 | export const schema: { types: SchemaTypeDefinition[] } = { 7 | types: [author, project, playlist], 8 | } 9 | -------------------------------------------------------------------------------- /sanity/lib/client.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from 'next-sanity' 2 | 3 | import { apiVersion, dataset, projectId } from '../env' 4 | 5 | export const client = createClient({ 6 | projectId, 7 | dataset, 8 | apiVersion, 9 | useCdn: true, // Set to false if statically generating pages, using ISR or tag-based revalidation 10 | }) 11 | -------------------------------------------------------------------------------- /app/api/sentry-example-api/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | 3 | export const dynamic = "force-dynamic"; 4 | 5 | // A faulty API route to test Sentry's error monitoring 6 | export function GET() { 7 | throw new Error("Sentry Example API Route Error"); 8 | return NextResponse.json({ data: "Testing Sentry Error..." }); 9 | } 10 | -------------------------------------------------------------------------------- /public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sanity/structure.ts: -------------------------------------------------------------------------------- 1 | import type {StructureResolver} from 'sanity/structure' 2 | 3 | // https://www.sanity.io/docs/structure-builder-cheat-sheet 4 | export const structure: StructureResolver = (S) => 5 | S.list() 6 | .title('Content') 7 | .items([ 8 | S.documentTypeListItem("author").title("Author"), 9 | S.documentTypeListItem("project").title("Project"), 10 | ]) 11 | -------------------------------------------------------------------------------- /instrumentation.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from '@sentry/nextjs'; 2 | 3 | export async function register() { 4 | if (process.env.NEXT_RUNTIME === 'nodejs') { 5 | await import('./sentry.server.config'); 6 | } 7 | 8 | if (process.env.NEXT_RUNTIME === 'edge') { 9 | await import('./sentry.edge.config'); 10 | } 11 | } 12 | 13 | export const onRequestError = Sentry.captureRequestError; 14 | -------------------------------------------------------------------------------- /sanity.cli.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This configuration file lets you run `$ sanity [command]` in this folder 3 | * Go to https://www.sanity.io/docs/cli to learn more. 4 | **/ 5 | import { defineCliConfig } from 'sanity/cli' 6 | 7 | const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID 8 | const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET 9 | 10 | export default defineCliConfig({ api: { projectId, dataset } }) 11 | -------------------------------------------------------------------------------- /sanity/lib/write-client.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "next-sanity" 2 | import "server-only" 3 | import { apiVersion, dataset, projectId, token } from "../env" 4 | 5 | export const writeClient = createClient({ 6 | projectId, 7 | dataset, 8 | apiVersion, 9 | useCdn: false, 10 | token 11 | }); 12 | 13 | if(!writeClient.config().token){ 14 | throw new Error('Sanity Write Token Not Found!'); 15 | } -------------------------------------------------------------------------------- /sanity/lib/image.ts: -------------------------------------------------------------------------------- 1 | import createImageUrlBuilder from '@sanity/image-url' 2 | import { SanityImageSource } from "@sanity/image-url/lib/types/types"; 3 | 4 | import { dataset, projectId } from '../env' 5 | 6 | // https://www.sanity.io/docs/image-url 7 | const builder = createImageUrlBuilder({ projectId, dataset }) 8 | 9 | export const urlFor = (source: SanityImageSource) => { 10 | return builder.image(source) 11 | } 12 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { FlatCompat } from "@eslint/eslintrc"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.extends("next/core-web-vitals", "next/typescript"), 14 | ]; 15 | 16 | export default eslintConfig; 17 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 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 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | 8 | 9 | export function formateDate(date : string){ 10 | return new Date(date).toLocaleDateString('en-IN', { 11 | month : 'long', 12 | day : 'numeric', 13 | year : 'numeric' 14 | }) 15 | } 16 | 17 | 18 | export function parseServerActionResponse(response : T){ 19 | return JSON.parse(JSON.stringify(response)); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /app/(root)/project/create/page.tsx: -------------------------------------------------------------------------------- 1 | import { auth } from '@/auth' 2 | import ProjectForm from '@/components/ProjectForm' 3 | import { redirect } from 'next/navigation'; 4 | 5 | import React from 'react' 6 | 7 | const Create = async () => { 8 | const session = await auth(); 9 | if(!session) redirect("/"); 10 | 11 | return ( 12 | <> 13 |
14 |

15 | Submit Your Project 16 |

17 |
18 | 19 | 20 | ) 21 | } 22 | 23 | export default Create -------------------------------------------------------------------------------- /components/SearchFormReset.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import { X } from 'lucide-react'; 3 | import Link from 'next/link'; 4 | import React from 'react' 5 | 6 | const SearchFormReset = () => { 7 | const reset = () =>{ 8 | const form = document.querySelector('search-form') as HTMLFormElement; 9 | if (form) form.reset(); 10 | } 11 | return ( 12 | 17 | ) 18 | } 19 | 20 | export default SearchFormReset -------------------------------------------------------------------------------- /lib/validation.ts: -------------------------------------------------------------------------------- 1 | import {z} from "zod"; 2 | 3 | export const formSchema = z.object({ 4 | title :z.string().min(3).max(100), 5 | description :z.string().min(5).max(500), 6 | category :z.string().min(3).max(20), 7 | link : z.string().url().refine(async (url)=>{ 8 | try { 9 | const res = await fetch(url, {method: "HEAD"}); 10 | const contentType = res.headers.get('content-type'); 11 | return contentType?.startsWith("image/"); 12 | } catch (error) { 13 | return false; 14 | } 15 | }), 16 | details : z.string().min(10) 17 | }) -------------------------------------------------------------------------------- /sanity/lib/live.ts: -------------------------------------------------------------------------------- 1 | // Querying with "sanityFetch" will keep content automatically updated 2 | // Before using it, import and render "" in your layout, see 3 | // https://github.com/sanity-io/next-sanity#live-content-api for more information. 4 | import { defineLive } from "next-sanity"; 5 | import { client } from './client' 6 | 7 | export const { sanityFetch, SanityLive } = defineLive({ 8 | client: client.withConfig({ 9 | // Live content is currently only available on the experimental API 10 | // https://www.sanity.io/docs/api-versioning 11 | apiVersion: 'vX' 12 | }) 13 | }); 14 | -------------------------------------------------------------------------------- /sanity/env.ts: -------------------------------------------------------------------------------- 1 | export const apiVersion = 2 | process.env.NEXT_PUBLIC_SANITY_API_VERSION || '2024-12-16' 3 | 4 | export const dataset = assertValue( 5 | process.env.NEXT_PUBLIC_SANITY_DATASET, 6 | 'Missing environment variable: NEXT_PUBLIC_SANITY_DATASET' 7 | ) 8 | 9 | export const projectId = assertValue( 10 | process.env.NEXT_PUBLIC_SANITY_PROJECT_ID, 11 | 'Missing environment variable: NEXT_PUBLIC_SANITY_PROJECT_ID' 12 | ) 13 | 14 | export const token = process.env.SANITY_WRITE_CLIENT; 15 | 16 | function assertValue(v: T | undefined, errorMessage: string): T { 17 | if (v === undefined) { 18 | throw new Error(errorMessage) 19 | } 20 | 21 | return v 22 | } 23 | -------------------------------------------------------------------------------- /sanity/schemaTypes/playlist.ts: -------------------------------------------------------------------------------- 1 | import { defineField, defineType } from "sanity"; 2 | 3 | export const playlist = defineType({ 4 | name : 'playlist', 5 | title : 'Playlist', 6 | type : 'document', 7 | fields : [ 8 | defineField({ 9 | name : 'slug', 10 | type : 'slug', 11 | options : { 12 | source : 'title' 13 | } 14 | }), 15 | defineField({ 16 | name : 'title', 17 | type : 'string' 18 | }), 19 | defineField({ 20 | name : 'select', 21 | type : 'array', 22 | of : [{type : 'reference', to:[{type :'project'}]}] 23 | }) 24 | ] 25 | }) -------------------------------------------------------------------------------- /sentry.server.config.ts: -------------------------------------------------------------------------------- 1 | // This file configures the initialization of Sentry on the server. 2 | // The config you add here will be used whenever the server handles a request. 3 | // https://docs.sentry.io/platforms/javascript/guides/nextjs/ 4 | 5 | import * as Sentry from "@sentry/nextjs"; 6 | 7 | Sentry.init({ 8 | dsn: "https://a7c2ec688756e2a7fd6ceeb7c3c053b6@o4508431288238080.ingest.us.sentry.io/4508431302721536", 9 | 10 | // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. 11 | tracesSampleRate: 1, 12 | 13 | // Setting this option to true will print useful information to the console while you're setting up Sentry. 14 | debug: false, 15 | }); 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /.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.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | 43 | # Sentry Config File 44 | .env.sentry-build-plugin 45 | -------------------------------------------------------------------------------- /app/studio/[[...tool]]/page.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This route is responsible for the built-in authoring environment using Sanity Studio. 3 | * All routes under your studio path is handled by this file using Next.js' catch-all routes: 4 | * https://nextjs.org/docs/routing/dynamic-routes#catch-all-routes 5 | * 6 | * You can learn more about the next-sanity package here: 7 | * https://github.com/sanity-io/next-sanity 8 | */ 9 | 10 | import { NextStudio } from 'next-sanity/studio' 11 | import config from '../../../sanity.config' 12 | 13 | export const dynamic = 'force-static' 14 | 15 | export { metadata, viewport } from 'next-sanity/studio' 16 | 17 | export default function StudioPage() { 18 | return 19 | } 20 | -------------------------------------------------------------------------------- /components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Textarea = React.forwardRef< 6 | HTMLTextAreaElement, 7 | React.ComponentProps<"textarea"> 8 | >(({ className, ...props }, ref) => { 9 | return ( 10 |