├── .cursor └── rules │ └── posthog-integration.mdc ├── .env.example ├── .gitignore ├── Dockerfile ├── README.md ├── bun.lockb ├── components.json ├── eslint.config.mjs ├── next.config.ts ├── package.json ├── pnpm-lock.yaml ├── postcss.config.mjs ├── prettier.config.js ├── prisma ├── migrations │ ├── 20250212201806_init │ │ └── migration.sql │ ├── 20250305120831_post_and_nerd_added │ │ └── migration.sql │ ├── 20250305121810_post_naming │ │ └── migration.sql │ ├── 20250305123652_changes_title │ │ └── migration.sql │ ├── 20250312204245_access_post │ │ └── migration.sql │ ├── 20250312205052_default_access_post │ │ └── migration.sql │ ├── 20250313090028_user_about │ │ └── migration.sql │ ├── 20250313141440_first_time │ │ └── migration.sql │ ├── 20250313144606_country_add_user │ │ └── migration.sql │ ├── 20250315130859_post_commment_added │ │ └── migration.sql │ ├── 20250315133143_fix_post_comment │ │ └── migration.sql │ ├── 20250317204630_like_and_bookmark_post │ │ └── migration.sql │ ├── 20250317210229_bookmark_and_like │ │ └── migration.sql │ ├── 20250317214043_removed_bookmark_and_like │ │ └── migration.sql │ ├── 20250317214332_postlike │ │ └── migration.sql │ ├── 20250318084831_like │ │ └── migration.sql │ ├── 20250318092209_delete_relation_bookmark_and_like │ │ └── migration.sql │ ├── 20250319105708_report_added │ │ └── migration.sql │ ├── 20250320074027_media_added │ │ └── migration.sql │ ├── 20250320125628_midea_post_delete │ │ └── migration.sql │ ├── 20250324102053_cover_image │ │ └── migration.sql │ ├── 20250324103643_cover_image │ │ └── migration.sql │ ├── 20250324125706_add_follow_feature │ │ └── migration.sql │ ├── 20250324144631_following_added │ │ └── migration.sql │ ├── 20250325172417_add_projects_schema │ │ └── migration.sql │ ├── 20250326111725_categoty │ │ └── migration.sql │ ├── 20250326153217_project_image │ │ └── migration.sql │ ├── 20250328102830_user_id_inprojectupdate │ │ └── migration.sql │ ├── 20250329092445_deletion_for_reply_comments │ │ └── migration.sql │ ├── 20250329093601_share_project_update_as_post │ │ └── migration.sql │ ├── 20250329093736_share_project_update_as_post_update │ │ └── migration.sql │ ├── 20250329101716_share_project_update_as_post_2 │ │ └── migration.sql │ ├── 20250331152000_community │ │ └── migration.sql │ ├── 20250409085146_following_follower_fixed │ │ └── migration.sql │ ├── 20250409101402_nerd_unique │ │ └── migration.sql │ ├── 20250409233203_add_original_post_id │ │ └── migration.sql │ ├── 20250409235112_sharepost │ │ └── migration.sql │ ├── 20250410005214_backup │ │ └── migration.sql │ ├── 20250410014100_working │ │ └── migration.sql │ ├── 20250411135652_add_shared_post_model │ │ └── migration.sql │ ├── 20250411140858_reverse │ │ └── migration.sql │ ├── 20250411141355_add_additional_context_to_report │ │ └── migration.sql │ ├── 20250419062030_added_bug_report │ │ └── migration.sql │ ├── 20250421124920_added_notification │ │ └── migration.sql │ ├── 20250421153152_add_notifications │ │ └── migration.sql │ ├── 20250422072157_updare │ │ └── migration.sql │ ├── 20250422090303_notification │ │ └── migration.sql │ ├── 20250422093720_update_nerdspace │ │ └── migration.sql │ └── migration_lock.toml └── schema.prisma ├── public ├── 01.png ├── 03.png ├── 04.png ├── 05.png ├── Elon.png ├── Enistine.png ├── adawa.webp ├── art.jpg ├── auth-bg.png ├── bg9.jpg ├── build.jpg ├── fashion.jpg ├── file.svg ├── fonts │ ├── ITCGaramondStd-BkCond.ttf │ ├── ITCGaramondStd-LtCond.ttf │ ├── ITCGaramondStd-LtCondIta.ttf │ └── user.jpg ├── game.jpg ├── globe.svg ├── gwax.jpg ├── hu.webp ├── logo.jpg ├── music.jpg ├── nerd.jpg ├── next.svg ├── obsession.jpg ├── robot.jpg ├── user.jpg ├── user_placeholder.jpg ├── vercel.svg └── window.svg ├── src ├── api │ └── notification.ts ├── app │ ├── (app) │ │ ├── ai │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── community │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── event │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── explore │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ ├── page.tsx │ │ ├── profile │ │ │ ├── ProjectsTab.tsx │ │ │ ├── [userId] │ │ │ │ ├── followers │ │ │ │ │ └── page.tsx │ │ │ │ ├── following │ │ │ │ │ └── page.tsx │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── page.tsx │ │ │ └── user │ │ │ │ └── [userId] │ │ │ │ ├── followers │ │ │ │ └── page.tsx │ │ │ │ └── following │ │ │ │ └── page.tsx │ │ ├── project │ │ │ ├── [id] │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── sandbox │ │ │ └── page.tsx │ │ ├── user-profile │ │ │ ├── [userId] │ │ │ │ ├── followers │ │ │ │ │ └── page.tsx │ │ │ │ ├── following │ │ │ │ │ └── page.tsx │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ └── whotofollow │ │ │ └── page.tsx │ ├── (auth) │ │ ├── forget-password │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ ├── login │ │ │ └── page.tsx │ │ ├── reset-password │ │ │ └── page.tsx │ │ ├── signup │ │ │ └── page.tsx │ │ └── story │ │ │ └── page.tsx │ ├── api │ │ ├── account │ │ │ └── route.ts │ │ ├── auth │ │ │ └── [...all] │ │ │ │ └── route.ts │ │ ├── bug │ │ │ └── route.ts │ │ ├── community │ │ │ ├── category │ │ │ │ └── route.ts │ │ │ └── route.ts │ │ ├── dummy │ │ │ └── route.ts │ │ ├── explore │ │ │ └── route.ts │ │ ├── media-upload │ │ │ └── route.ts │ │ ├── notification │ │ │ └── route.ts │ │ ├── onboarding │ │ │ ├── route.ts │ │ │ └── status │ │ │ │ └── route.ts │ │ ├── post │ │ │ ├── [id] │ │ │ │ └── route.ts │ │ │ ├── bookmark │ │ │ │ └── route.ts │ │ │ ├── comment │ │ │ │ └── route.ts │ │ │ ├── like │ │ │ │ └── route.ts │ │ │ ├── report │ │ │ │ └── route.ts │ │ │ └── route.ts │ │ ├── project │ │ │ ├── [id] │ │ │ │ ├── route.ts │ │ │ │ └── updates │ │ │ │ │ └── route.ts │ │ │ ├── follow │ │ │ │ └── route.ts │ │ │ ├── notification │ │ │ │ └── route.ts │ │ │ ├── rank │ │ │ │ └── route.ts │ │ │ ├── recommendation │ │ │ │ └── route.ts │ │ │ ├── review │ │ │ │ └── route.ts │ │ │ ├── route.ts │ │ │ ├── star │ │ │ │ └── route.ts │ │ │ └── update │ │ │ │ ├── [id] │ │ │ │ └── route.ts │ │ │ │ ├── comment │ │ │ │ └── route.ts │ │ │ │ ├── like │ │ │ │ └── route.ts │ │ │ │ └── share │ │ │ │ └── route.ts │ │ ├── route.ts │ │ ├── security │ │ │ └── route.ts │ │ ├── user │ │ │ ├── follow-notification │ │ │ │ └── route.ts │ │ │ ├── follow │ │ │ │ ├── recommendation │ │ │ │ │ └── route.ts │ │ │ │ └── route.ts │ │ │ ├── posts │ │ │ │ └── route.ts │ │ │ └── route.ts │ │ ├── users │ │ │ ├── [userId] │ │ │ │ ├── counts │ │ │ │ │ └── route.ts │ │ │ │ ├── followers │ │ │ │ │ └── route.ts │ │ │ │ ├── following │ │ │ │ │ └── route.ts │ │ │ │ ├── projects │ │ │ │ │ └── route.ts │ │ │ │ └── route.ts │ │ │ ├── bookmarks │ │ │ │ └── route.ts │ │ │ ├── check-follow │ │ │ │ └── route.ts │ │ │ ├── followers │ │ │ │ └── route.ts │ │ │ ├── following │ │ │ │ └── route.ts │ │ │ ├── projects │ │ │ │ ├── owned │ │ │ │ │ └── route.ts │ │ │ │ └── route.ts │ │ │ └── route.ts │ │ └── whoami │ │ │ ├── post │ │ │ └── route.ts │ │ │ ├── private │ │ │ └── route.ts │ │ │ └── route.ts │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ ├── onboarding │ │ └── page.tsx │ ├── post │ │ └── [id] │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ ├── projects │ │ └── recommendations │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ └── settings │ │ ├── layout.tsx │ │ └── page.tsx ├── components │ ├── FeedbackButton.tsx │ ├── FollowList.tsx │ ├── NotificationDropdown.tsx │ ├── UserList.tsx │ ├── UserListSkeleton.tsx │ ├── app-sidebar.tsx │ ├── country-selector.tsx │ ├── create-post-form.tsx │ ├── custom │ │ └── image-upload.tsx │ ├── explore │ │ ├── explore-cards.tsx │ │ ├── explore-entry.tsx │ │ ├── explore-post-card.tsx │ │ ├── explore-render-post.tsx │ │ └── project-explore-card.tsx │ ├── follow-button.tsx │ ├── forget-password.tsx │ ├── get-started-form.tsx │ ├── image-preview.tsx │ ├── login-form.tsx │ ├── media │ │ ├── media-uploader.tsx │ │ └── post-file-uploader.tsx │ ├── mobile-view-message.tsx │ ├── modal │ │ ├── delete.modal.tsx │ │ ├── edit.modal.tsx │ │ └── report.modal.tsx │ ├── nav-main.tsx │ ├── nav-projects.tsx │ ├── nav-secondary.tsx │ ├── nav-user.tsx │ ├── navbar.tsx │ ├── navbar │ │ ├── community_navbar.tsx │ │ ├── left-navbar.tsx │ │ ├── mobile-nav-bar.tsx │ │ └── right-navbar.tsx │ ├── onboarding-form.tsx │ ├── onboarding │ │ └── onboarding-prompt.tsx │ ├── phone-input.tsx │ ├── post-card.tsx │ ├── post │ │ ├── PostCard.tsx │ │ ├── comment │ │ │ ├── DeleteCommentModal.tsx │ │ │ ├── EditCommentModal.tsx │ │ │ ├── comment.tsx │ │ │ └── render-comments.tsx │ │ ├── post-card.tsx │ │ ├── post-input.tsx │ │ └── render-post.tsx │ ├── project │ │ ├── followed-projects.tsx │ │ ├── leaderBorad.tsx │ │ ├── project-card.tsx │ │ ├── project-component.tsx │ │ ├── project-detail.tsx │ │ └── project-update-card.tsx │ ├── quality-notice.tsx │ ├── reset-password.tsx │ ├── search-form.tsx │ ├── settings-dialog.tsx │ ├── settings │ │ ├── account-setting-skeleton.tsx │ │ ├── account-setting.tsx │ │ ├── follow-button.tsx │ │ ├── followers-following-list.tsx │ │ ├── my-private-post.tsx │ │ ├── myposts.tsx │ │ ├── notification-setting.tsx │ │ ├── profile-setting.tsx │ │ ├── profile.tsx │ │ ├── tabs │ │ │ ├── BookmarksTab.tsx │ │ │ ├── CollectionsTab.tsx │ │ │ ├── PrivateTab.tsx │ │ │ ├── ProjectsTab.tsx │ │ │ └── UserProjectsTab.tsx │ │ ├── therms-conditions.tsx │ │ ├── user-posts.tsx │ │ ├── user-profile-stats.tsx │ │ └── user-profile.tsx │ ├── sidebar │ │ ├── nav-main.tsx │ │ ├── nav-projects.tsx │ │ ├── nav-secondary.tsx │ │ └── nav-user.tsx │ ├── signup-form.tsx │ ├── site-header.tsx │ ├── skeleton │ │ ├── comment.skelton.tsx │ │ ├── morepostFetch.skeleton.tsx │ │ ├── project-card.tsx │ │ ├── project-detail.skeleton.tsx │ │ └── render-post.skeleton.tsx │ ├── soon │ │ ├── nerdspace-out.tsx │ │ └── soon.tsx │ ├── theme-provider.tsx │ ├── theme-toggle.tsx │ ├── toaster.tsx │ ├── ui │ │ ├── accordion.tsx │ │ ├── alert-dialog.tsx │ │ ├── alert.tsx │ │ ├── aspect-ratio.tsx │ │ ├── avatar.tsx │ │ ├── badge.tsx │ │ ├── breadcrumb.tsx │ │ ├── button.tsx │ │ ├── calendar.tsx │ │ ├── card.tsx │ │ ├── carousel.tsx │ │ ├── chart.tsx │ │ ├── checkbox.tsx │ │ ├── collapsible.tsx │ │ ├── command.tsx │ │ ├── context-menu.tsx │ │ ├── country-dropdown.tsx │ │ ├── dialog.tsx │ │ ├── drawer.tsx │ │ ├── dropdown-menu.tsx │ │ ├── edit-project-dialog.tsx │ │ ├── emoji-picker.tsx │ │ ├── form.tsx │ │ ├── hover-card.tsx │ │ ├── image-upload.tsx │ │ ├── input-otp.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── loading-spinner.tsx │ │ ├── menubar.tsx │ │ ├── navigation-menu.tsx │ │ ├── pagination.tsx │ │ ├── popover.tsx │ │ ├── progress.tsx │ │ ├── radio-group.tsx │ │ ├── resizable.tsx │ │ ├── resizeble-text-area.tsx │ │ ├── scroll-area.tsx │ │ ├── select.tsx │ │ ├── separator.tsx │ │ ├── sheet.tsx │ │ ├── sidebar.tsx │ │ ├── skeleton.tsx │ │ ├── slider.tsx │ │ ├── sonner.tsx │ │ ├── switch.tsx │ │ ├── table.tsx │ │ ├── tabs.tsx │ │ ├── textarea.tsx │ │ ├── toast.tsx │ │ ├── toaster.tsx │ │ ├── toggle-group.tsx │ │ ├── toggle.tsx │ │ └── tooltip.tsx │ └── user │ │ ├── FollowListSkeleton.tsx │ │ ├── community.tsx │ │ ├── followList.tsx │ │ ├── follower.tsx │ │ ├── following.tsx │ │ ├── recommend-project-page.tsx │ │ ├── recommend-project.tsx │ │ └── trending.tsx ├── functions │ ├── access-change-post.ts │ ├── calculate-time-difference.ts │ ├── color-extractor.ts │ ├── create-post.ts │ ├── delete-post.ts │ ├── edit-post.ts │ ├── fetch-my-post.ts │ ├── fetch-my-private-post.ts │ ├── fetch-post.ts │ ├── fetchProject.ts │ ├── follow.ts │ ├── format-number.ts │ ├── get-notification.ts │ ├── get-user.ts │ ├── get-who-am-i.ts │ ├── like-post.ts │ └── render-helper.ts ├── hooks │ ├── use-communities.ts │ ├── use-community-posts.ts │ ├── use-debounce.ts │ ├── use-mobile.tsx │ ├── use-toast-notifications.ts │ ├── use-toast.ts │ └── useFetchPosts.tsx ├── interface │ └── auth │ │ ├── comment.interface.ts │ │ ├── community.interface.ts │ │ ├── onboarding.interface.ts │ │ ├── post.interface.ts │ │ ├── project.interface.ts │ │ ├── projectInterface │ │ ├── signin.interface.ts │ │ ├── signup.interface.ts │ │ └── user.interface.ts ├── lib │ ├── auth-client.ts │ ├── auth.ts │ ├── axios.ts │ ├── emailTemplates │ │ ├── baseTemplate.ts │ │ └── templates.ts │ ├── posthog-provider.tsx │ ├── posthog.ts │ ├── prisma.ts │ ├── providers.tsx │ ├── sendEmail.ts │ ├── types.ts │ ├── utils.ts │ ├── validations │ │ └── notification.ts │ └── validators │ │ └── community.ts ├── middleware.ts ├── providers │ ├── day-picker-provider.tsx │ ├── tanstack-query-provider.tsx │ └── who-am-i-provider.tsx ├── query │ └── post-query.ts ├── script │ └── dummy-users.ts ├── store │ ├── community.store.ts │ ├── data.store.ts │ ├── explore.store.ts │ ├── fileStore.ts │ ├── post.store.ts │ ├── project-update.store.ts │ ├── report.strore.ts │ ├── search.store.ts │ ├── useFormStore.ts │ ├── user.store.ts │ └── userProfile.store.ts ├── types │ └── apiResponses.ts └── validation │ ├── bookmark.validation.ts │ ├── bug.validation.ts │ ├── comment.validation.ts │ ├── like.validation.ts │ ├── login.validation.ts │ ├── post.validation.ts │ ├── project.validation.ts │ ├── report.validation.ts │ ├── reset-pass.validation.ts │ ├── signup.validation.ts │ └── verify-email.ts ├── tailwind.config.ts ├── tsconfig.json └── vercel.json /.cursor/rules/posthog-integration.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: apply when interacting with PostHog/analytics tasks 3 | globs: 4 | alwaysApply: true 5 | --- 6 | 7 | Never hallucinate an API key. Instead, always use the API key populated in the .env file. 8 | 9 | # Feature flags 10 | 11 | A given feature flag should be used in as few places as possible. Do not increase the risk of undefined behavior by scattering the same feature flag across multiple areas of code. If the same feature flag needs to be introduced at multiple callsites, flag this for the developer to inspect carefully. 12 | 13 | If a job requires creating new feature flag names, make them as clear and descriptive as possible. 14 | 15 | If using TypeScript, use an enum to store flag names. If using JavaScript, store flag names as strings to an object declared as a constant, to simulate an enum. Use a consistent naming convention for this storage. enum/const object members should be written UPPERCASE_WITH_UNDERSCORE. 16 | 17 | Gate flag-dependent code on a check that verifies the flag's values are valid and expected. 18 | 19 | # Custom properties 20 | 21 | If a custom property for a person or event is at any point referenced in two or more files or two or more callsites in the same file, use an enum or const object, as above in feature flags. 22 | 23 | # Naming 24 | 25 | Before creating any new event or property names, consult with the developer for any existing naming convention. Consistency in naming is essential, and additional context may exist outside this project. Similarly, be careful about any changes to existing event and property names, as this may break reporting and distort data for the project. 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Database connection URL for PostgreSQL 2 | DATABASE_URL="postgresql://username:password@localhost:5432/database_name?schema=public" 3 | 4 | # Authentication configuration 5 | BETTER_AUTH_SECRET=your_32_character_secret_key_here 6 | BETTER_AUTH_URL=http://localhost:3000 7 | 8 | # GitHub OAuth credentials 9 | GITHUB_CLIENT_ID=your_github_client_id 10 | GITHUB_CLIENT_SECRET=your_github_client_secret 11 | 12 | # Cloudinary configuration for image uploads 13 | NEXT_PUBLIC_CLOUDINARY_UPLOAD_URL=your_cloudinary_upload_url 14 | NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET=your_upload_preset_name 15 | CLOUDINARY_URL=cloudinary://api_key:api_secret@cloud_name 16 | 17 | # Base URL for the application 18 | # NEXT_PUBLIC_BASE_URL=http://localhost:3000 19 | 20 | # Environment mode 21 | NODE_ENV=development 22 | 23 | # Email configuration for sending emails 24 | EMAIL_USER=your_email@example.com 25 | EMAIL_PASS=your_email_app_specific_password || if_enableed_2factor_auth_you'll_need_to_generate_app_specific_password 26 | 27 | # PostHog analytics key 28 | NEXT_PUBLIC_POSTHOG_KEY=your_posthog_project_key || if_analytics_are_enabled_in_posthog_dashboard -------------------------------------------------------------------------------- /.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 | /dist 23 | 24 | # misc 25 | .DS_Store 26 | *.pem 27 | .idea/ 28 | *.log 29 | *.lock 30 | .cache 31 | .env 32 | .env.local 33 | .env.development.local 34 | .env.test.local 35 | .env.production.local 36 | 37 | # debug 38 | npm-debug.log* 39 | yarn-debug.log* 40 | yarn-error.log* 41 | .pnpm-debug.log* 42 | 43 | # vercel 44 | .vercel 45 | 46 | # typescript 47 | *.tsbuildinfo 48 | next-env.d.ts 49 | 50 | # Optional 51 | .turbo 52 | .swc/ 53 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/Dockerfile -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. 37 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/bun.lockb -------------------------------------------------------------------------------- /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": "src/app/globals.css", 9 | "baseColor": "zinc", 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 | } -------------------------------------------------------------------------------- /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", "prettier"), 14 | ...compat.config({ 15 | rules: { 16 | "@typescript-eslint/no-unused-vars": "off", 17 | "@typescript-eslint/no-explicit-any": "off", 18 | }, 19 | }), 20 | ]; 21 | 22 | export default eslintConfig; 23 | -------------------------------------------------------------------------------- /next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | images: { 6 | domains: [ 7 | "avatars.githubusercontent.com", 8 | "res.cloudinary.com", 9 | "images.unsplash.com", 10 | "play-lh.googleusercontent.com", 11 | ], 12 | // loader: "cloudinary", 13 | // path: "https://api.cloudinary.com/v1_1/dsaitxphg/image/upload", 14 | }, 15 | 16 | // We remove ESLint 17 | // run Biome and TypeScript separately in the CI pipeline 18 | // eslint: { 19 | // ignoreDuringBuilds: true, 20 | // }, 21 | // typescript: { 22 | // ignoreBuildErrors: true, 23 | // }, 24 | 25 | experimental: { 26 | // nodeMiddleware: true, 27 | staleTimes: { 28 | dynamic: 30, 29 | }, 30 | }, 31 | async rewrites() { 32 | return [ 33 | { 34 | source: "/ingest/static/:path*", 35 | destination: "https://us-assets.i.posthog.com/static/:path*", 36 | }, 37 | { 38 | source: "/ingest/:path*", 39 | destination: "https://us.i.posthog.com/:path*", 40 | }, 41 | { 42 | source: "/ingest/decide", 43 | destination: "https://us.i.posthog.com/decide", 44 | }, 45 | ]; 46 | }, 47 | // This is required to support PostHog trailing slash API requests 48 | skipTrailingSlashRedirect: true, 49 | }; 50 | 51 | export default nextConfig; 52 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ["prettier-plugin-tailwindcss"], 3 | }; 4 | -------------------------------------------------------------------------------- /prisma/migrations/20250305120831_post_and_nerd_added/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "user" ADD COLUMN "nerdAt" TEXT; 3 | 4 | -- CreateTable 5 | CREATE TABLE "post" ( 6 | "id" TEXT NOT NULL, 7 | "content" TEXT NOT NULL, 8 | "createdAt" TIMESTAMP(3) NOT NULL, 9 | "updatedAt" TIMESTAMP(3) NOT NULL, 10 | "userId" TEXT NOT NULL, 11 | 12 | CONSTRAINT "post_pkey" PRIMARY KEY ("id") 13 | ); 14 | 15 | -- AddForeignKey 16 | ALTER TABLE "post" ADD CONSTRAINT "post_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 17 | -------------------------------------------------------------------------------- /prisma/migrations/20250305121810_post_naming/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the `post` table. If the table is not empty, all the data it contains will be lost. 5 | 6 | */ 7 | -- DropForeignKey 8 | ALTER TABLE "post" DROP CONSTRAINT "post_userId_fkey"; 9 | 10 | -- DropTable 11 | DROP TABLE "post"; 12 | 13 | -- CreateTable 14 | CREATE TABLE "posts" ( 15 | "id" TEXT NOT NULL, 16 | "content" TEXT NOT NULL, 17 | "createdAt" TIMESTAMP(3) NOT NULL, 18 | "updatedAt" TIMESTAMP(3) NOT NULL, 19 | "userId" TEXT NOT NULL, 20 | 21 | CONSTRAINT "posts_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 CASCADE ON UPDATE CASCADE; 26 | -------------------------------------------------------------------------------- /prisma/migrations/20250305123652_changes_title/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "posts" ALTER COLUMN "createdAt" SET DEFAULT CURRENT_TIMESTAMP, 3 | ALTER COLUMN "updatedAt" SET DEFAULT CURRENT_TIMESTAMP; 4 | -------------------------------------------------------------------------------- /prisma/migrations/20250312204245_access_post/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - Added the required column `access` to the `posts` table without a default value. This is not possible if the table is not empty. 5 | 6 | */ 7 | -- CreateEnum 8 | CREATE TYPE "PostAccess" AS ENUM ('private', 'public'); 9 | 10 | -- AlterTable 11 | ALTER TABLE "posts" ADD COLUMN "access" "PostAccess" NOT NULL; 12 | -------------------------------------------------------------------------------- /prisma/migrations/20250312205052_default_access_post/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "posts" ALTER COLUMN "access" SET DEFAULT 'public'; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20250313090028_user_about/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "user" ADD COLUMN "bio" TEXT, 3 | ADD COLUMN "country" TEXT, 4 | ADD COLUMN "link" TEXT, 5 | ADD COLUMN "visualName" TEXT; 6 | -------------------------------------------------------------------------------- /prisma/migrations/20250313141440_first_time/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "user" ADD COLUMN "firstTime" BOOLEAN NOT NULL DEFAULT true; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20250313144606_country_add_user/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `country` on the `user` table. All the data in the column will be lost. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "user" DROP COLUMN "country", 9 | ADD COLUMN "countryId" TEXT; 10 | 11 | -- CreateTable 12 | CREATE TABLE "country" ( 13 | "id" TEXT NOT NULL, 14 | "alpha2" TEXT NOT NULL, 15 | "alpha3" TEXT NOT NULL, 16 | "countryCallingCodes" TEXT[], 17 | "currencies" TEXT[], 18 | "emoji" TEXT, 19 | "ioc" TEXT NOT NULL, 20 | "languages" TEXT[], 21 | "name" TEXT NOT NULL, 22 | "status" TEXT NOT NULL, 23 | "userId" TEXT NOT NULL, 24 | 25 | CONSTRAINT "country_pkey" PRIMARY KEY ("id") 26 | ); 27 | 28 | -- CreateIndex 29 | CREATE UNIQUE INDEX "country_userId_key" ON "country"("userId"); 30 | 31 | -- AddForeignKey 32 | ALTER TABLE "country" ADD CONSTRAINT "country_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 33 | -------------------------------------------------------------------------------- /prisma/migrations/20250315130859_post_commment_added/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "postcomments" ( 3 | "id" TEXT NOT NULL, 4 | "userId" TEXT NOT NULL, 5 | "postId" TEXT NOT NULL, 6 | "parentId" TEXT, 7 | "content" TEXT NOT NULL, 8 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 9 | 10 | CONSTRAINT "postcomments_pkey" PRIMARY KEY ("id") 11 | ); 12 | 13 | -- AddForeignKey 14 | ALTER TABLE "postcomments" ADD CONSTRAINT "postcomments_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "postcomments"("id") ON DELETE SET NULL ON UPDATE CASCADE; 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "postcomments" ADD CONSTRAINT "postcomments_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 18 | 19 | -- AddForeignKey 20 | ALTER TABLE "postcomments" ADD CONSTRAINT "postcomments_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE CASCADE ON UPDATE CASCADE; 21 | -------------------------------------------------------------------------------- /prisma/migrations/20250315133143_fix_post_comment/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "postcomments" ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20250317204630_like_and_bookmark_post/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "postcomments" ALTER COLUMN "updatedAt" DROP DEFAULT; 3 | 4 | -- CreateTable 5 | CREATE TABLE "postlikes" ( 6 | "id" TEXT NOT NULL, 7 | "userId" TEXT NOT NULL, 8 | "postId" TEXT NOT NULL, 9 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 10 | 11 | CONSTRAINT "postlikes_pkey" PRIMARY KEY ("id") 12 | ); 13 | 14 | -- CreateTable 15 | CREATE TABLE "postbookmarks" ( 16 | "id" TEXT NOT NULL, 17 | "userId" TEXT NOT NULL, 18 | "postId" TEXT NOT NULL, 19 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 20 | 21 | CONSTRAINT "postbookmarks_pkey" PRIMARY KEY ("id") 22 | ); 23 | 24 | -- AddForeignKey 25 | ALTER TABLE "postlikes" ADD CONSTRAINT "postlikes_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 26 | 27 | -- AddForeignKey 28 | ALTER TABLE "postlikes" ADD CONSTRAINT "postlikes_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE CASCADE ON UPDATE CASCADE; 29 | 30 | -- AddForeignKey 31 | ALTER TABLE "postbookmarks" ADD CONSTRAINT "postbookmarks_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 32 | 33 | -- AddForeignKey 34 | ALTER TABLE "postbookmarks" ADD CONSTRAINT "postbookmarks_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE CASCADE ON UPDATE CASCADE; 35 | -------------------------------------------------------------------------------- /prisma/migrations/20250317210229_bookmark_and_like/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - A unique constraint covering the columns `[userId,postId]` on the table `postbookmarks` will be added. If there are existing duplicate values, this will fail. 5 | - A unique constraint covering the columns `[userId,postId]` on the table `postlikes` will be added. If there are existing duplicate values, this will fail. 6 | 7 | */ 8 | -- CreateIndex 9 | CREATE UNIQUE INDEX "postbookmarks_userId_postId_key" ON "postbookmarks"("userId", "postId"); 10 | 11 | -- CreateIndex 12 | CREATE UNIQUE INDEX "postlikes_userId_postId_key" ON "postlikes"("userId", "postId"); 13 | -------------------------------------------------------------------------------- /prisma/migrations/20250317214043_removed_bookmark_and_like/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the `postbookmarks` table. If the table is not empty, all the data it contains will be lost. 5 | - You are about to drop the `postlikes` table. If the table is not empty, all the data it contains will be lost. 6 | 7 | */ 8 | -- DropForeignKey 9 | ALTER TABLE "postbookmarks" DROP CONSTRAINT "postbookmarks_postId_fkey"; 10 | 11 | -- DropForeignKey 12 | ALTER TABLE "postbookmarks" DROP CONSTRAINT "postbookmarks_userId_fkey"; 13 | 14 | -- DropForeignKey 15 | ALTER TABLE "postlikes" DROP CONSTRAINT "postlikes_postId_fkey"; 16 | 17 | -- DropForeignKey 18 | ALTER TABLE "postlikes" DROP CONSTRAINT "postlikes_userId_fkey"; 19 | 20 | -- DropTable 21 | DROP TABLE "postbookmarks"; 22 | 23 | -- DropTable 24 | DROP TABLE "postlikes"; 25 | -------------------------------------------------------------------------------- /prisma/migrations/20250317214332_postlike/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "postlikes" ( 3 | "id" TEXT NOT NULL, 4 | "userId" TEXT NOT NULL, 5 | "postId" TEXT NOT NULL, 6 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 7 | 8 | CONSTRAINT "postlikes_pkey" PRIMARY KEY ("id") 9 | ); 10 | 11 | -- CreateIndex 12 | CREATE UNIQUE INDEX "postlikes_userId_postId_key" ON "postlikes"("userId", "postId"); 13 | 14 | -- AddForeignKey 15 | ALTER TABLE "postlikes" ADD CONSTRAINT "postlikes_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 16 | 17 | -- AddForeignKey 18 | ALTER TABLE "postlikes" ADD CONSTRAINT "postlikes_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE CASCADE ON UPDATE CASCADE; 19 | -------------------------------------------------------------------------------- /prisma/migrations/20250318084831_like/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the `postlikes` table. If the table is not empty, all the data it contains will be lost. 5 | 6 | */ 7 | -- DropForeignKey 8 | ALTER TABLE "postlikes" DROP CONSTRAINT "postlikes_postId_fkey"; 9 | 10 | -- DropForeignKey 11 | ALTER TABLE "postlikes" DROP CONSTRAINT "postlikes_userId_fkey"; 12 | 13 | -- DropTable 14 | DROP TABLE "postlikes"; 15 | 16 | -- CreateTable 17 | CREATE TABLE "Like" ( 18 | "id" TEXT NOT NULL, 19 | "userId" TEXT NOT NULL, 20 | "postId" TEXT NOT NULL, 21 | 22 | CONSTRAINT "Like_pkey" PRIMARY KEY ("id") 23 | ); 24 | 25 | -- CreateTable 26 | CREATE TABLE "Bookmark" ( 27 | "id" TEXT NOT NULL, 28 | "userId" TEXT NOT NULL, 29 | "postId" TEXT NOT NULL, 30 | 31 | CONSTRAINT "Bookmark_pkey" PRIMARY KEY ("id") 32 | ); 33 | 34 | -- CreateIndex 35 | CREATE UNIQUE INDEX "Like_userId_postId_key" ON "Like"("userId", "postId"); 36 | 37 | -- CreateIndex 38 | CREATE UNIQUE INDEX "Bookmark_userId_postId_key" ON "Bookmark"("userId", "postId"); 39 | 40 | -- AddForeignKey 41 | ALTER TABLE "Like" ADD CONSTRAINT "Like_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 42 | 43 | -- AddForeignKey 44 | ALTER TABLE "Like" ADD CONSTRAINT "Like_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 45 | 46 | -- AddForeignKey 47 | ALTER TABLE "Bookmark" ADD CONSTRAINT "Bookmark_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 48 | 49 | -- AddForeignKey 50 | ALTER TABLE "Bookmark" ADD CONSTRAINT "Bookmark_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 51 | -------------------------------------------------------------------------------- /prisma/migrations/20250318092209_delete_relation_bookmark_and_like/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "Bookmark" DROP CONSTRAINT "Bookmark_postId_fkey"; 3 | 4 | -- DropForeignKey 5 | ALTER TABLE "Bookmark" DROP CONSTRAINT "Bookmark_userId_fkey"; 6 | 7 | -- DropForeignKey 8 | ALTER TABLE "Like" DROP CONSTRAINT "Like_postId_fkey"; 9 | 10 | -- DropForeignKey 11 | ALTER TABLE "Like" DROP CONSTRAINT "Like_userId_fkey"; 12 | 13 | -- AddForeignKey 14 | ALTER TABLE "Like" ADD CONSTRAINT "Like_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "Like" ADD CONSTRAINT "Like_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE CASCADE ON UPDATE CASCADE; 18 | 19 | -- AddForeignKey 20 | ALTER TABLE "Bookmark" ADD CONSTRAINT "Bookmark_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 21 | 22 | -- AddForeignKey 23 | ALTER TABLE "Bookmark" ADD CONSTRAINT "Bookmark_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE CASCADE ON UPDATE CASCADE; 24 | -------------------------------------------------------------------------------- /prisma/migrations/20250319105708_report_added/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "ReportStatus" AS ENUM ('PENDING', 'REVIEWED', 'RESOLVED', 'REJECTED'); 3 | 4 | -- CreateTable 5 | CREATE TABLE "reports" ( 6 | "id" TEXT NOT NULL, 7 | "reporterId" TEXT NOT NULL, 8 | "postId" TEXT, 9 | "commentId" TEXT, 10 | "reason" TEXT NOT NULL, 11 | "status" "ReportStatus" NOT NULL DEFAULT 'PENDING', 12 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 13 | "updatedAt" TIMESTAMP(3) NOT NULL, 14 | 15 | CONSTRAINT "reports_pkey" PRIMARY KEY ("id") 16 | ); 17 | 18 | -- AddForeignKey 19 | ALTER TABLE "reports" ADD CONSTRAINT "reports_reporterId_fkey" FOREIGN KEY ("reporterId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 20 | 21 | -- AddForeignKey 22 | ALTER TABLE "reports" ADD CONSTRAINT "reports_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE CASCADE ON UPDATE CASCADE; 23 | 24 | -- AddForeignKey 25 | ALTER TABLE "reports" ADD CONSTRAINT "reports_commentId_fkey" FOREIGN KEY ("commentId") REFERENCES "postcomments"("id") ON DELETE CASCADE ON UPDATE CASCADE; 26 | -------------------------------------------------------------------------------- /prisma/migrations/20250320074027_media_added/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "MediaType" AS ENUM ('IMAGE', 'VIDEO', 'GIF'); 3 | 4 | -- CreateTable 5 | CREATE TABLE "Media" ( 6 | "id" TEXT NOT NULL, 7 | "url" TEXT NOT NULL, 8 | "type" "MediaType" NOT NULL, 9 | "postId" TEXT NOT NULL, 10 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 11 | 12 | CONSTRAINT "Media_pkey" PRIMARY KEY ("id") 13 | ); 14 | 15 | -- AddForeignKey 16 | ALTER TABLE "Media" ADD CONSTRAINT "Media_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 17 | -------------------------------------------------------------------------------- /prisma/migrations/20250320125628_midea_post_delete/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "Media" DROP CONSTRAINT "Media_postId_fkey"; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "Media" ADD CONSTRAINT "Media_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE CASCADE ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /prisma/migrations/20250324102053_cover_image/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "posts" ADD COLUMN "coverImage" TEXT; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20250324103643_cover_image/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `coverImage` on the `posts` table. All the data in the column will be lost. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "posts" DROP COLUMN "coverImage"; 9 | 10 | -- AlterTable 11 | ALTER TABLE "user" ADD COLUMN "coverImage" TEXT; 12 | -------------------------------------------------------------------------------- /prisma/migrations/20250324125706_add_follow_feature/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Follow" ( 3 | "id" TEXT NOT NULL, 4 | "followerId" TEXT NOT NULL, 5 | "followingId" TEXT NOT NULL, 6 | 7 | CONSTRAINT "Follow_pkey" PRIMARY KEY ("id") 8 | ); 9 | 10 | -- CreateIndex 11 | CREATE UNIQUE INDEX "Follow_followerId_followingId_key" ON "Follow"("followerId", "followingId"); 12 | 13 | -- AddForeignKey 14 | ALTER TABLE "Follow" ADD CONSTRAINT "Follow_followerId_fkey" FOREIGN KEY ("followerId") REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "Follow" ADD CONSTRAINT "Follow_followingId_fkey" FOREIGN KEY ("followingId") REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 18 | -------------------------------------------------------------------------------- /prisma/migrations/20250324144631_following_added/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the `Follow` table. If the table is not empty, all the data it contains will be lost. 5 | 6 | */ 7 | -- DropForeignKey 8 | ALTER TABLE "Follow" DROP CONSTRAINT "Follow_followerId_fkey"; 9 | 10 | -- DropForeignKey 11 | ALTER TABLE "Follow" DROP CONSTRAINT "Follow_followingId_fkey"; 12 | 13 | -- DropTable 14 | DROP TABLE "Follow"; 15 | 16 | -- CreateTable 17 | CREATE TABLE "Follows" ( 18 | "id" TEXT NOT NULL, 19 | "followerId" TEXT NOT NULL, 20 | "followingId" TEXT NOT NULL, 21 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 22 | 23 | CONSTRAINT "Follows_pkey" PRIMARY KEY ("id") 24 | ); 25 | 26 | -- CreateIndex 27 | CREATE UNIQUE INDEX "Follows_followerId_followingId_key" ON "Follows"("followerId", "followingId"); 28 | 29 | -- AddForeignKey 30 | ALTER TABLE "Follows" ADD CONSTRAINT "Follows_followerId_fkey" FOREIGN KEY ("followerId") REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 31 | 32 | -- AddForeignKey 33 | ALTER TABLE "Follows" ADD CONSTRAINT "Follows_followingId_fkey" FOREIGN KEY ("followingId") REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 34 | -------------------------------------------------------------------------------- /prisma/migrations/20250326111725_categoty/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - The `category` column on the `Project` table would be dropped and recreated. This will lead to data loss if there is data in the column. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "Project" DROP COLUMN "category", 9 | ADD COLUMN "category" TEXT[]; 10 | -------------------------------------------------------------------------------- /prisma/migrations/20250326153217_project_image/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - Added the required column `image` to the `Project` table without a default value. This is not possible if the table is not empty. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "Project" ADD COLUMN "image" TEXT NOT NULL; 9 | -------------------------------------------------------------------------------- /prisma/migrations/20250328102830_user_id_inprojectupdate/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "ProjectUpdate" ADD COLUMN "userId" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "ProjectUpdate" ADD CONSTRAINT "ProjectUpdate_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /prisma/migrations/20250329092445_deletion_for_reply_comments/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "postcomments" DROP CONSTRAINT "postcomments_parentId_fkey"; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "postcomments" ADD CONSTRAINT "postcomments_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "postcomments"("id") ON DELETE CASCADE ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /prisma/migrations/20250329093601_share_project_update_as_post/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "posts" ADD COLUMN "projectId" TEXT, 3 | ADD COLUMN "shared" BOOLEAN; 4 | -------------------------------------------------------------------------------- /prisma/migrations/20250329093736_share_project_update_as_post_update/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "posts" ALTER COLUMN "shared" SET DEFAULT false; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20250329101716_share_project_update_as_post_2/migration.sql: -------------------------------------------------------------------------------- 1 | -- AddForeignKey 2 | ALTER TABLE "posts" ADD CONSTRAINT "posts_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE SET NULL ON UPDATE CASCADE; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20250409085146_following_follower_fixed/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "_UserInterests" ( 3 | "A" TEXT NOT NULL, 4 | "B" TEXT NOT NULL, 5 | 6 | CONSTRAINT "_UserInterests_AB_pkey" PRIMARY KEY ("A","B") 7 | ); 8 | 9 | -- CreateIndex 10 | CREATE INDEX "_UserInterests_B_index" ON "_UserInterests"("B"); 11 | 12 | -- AddForeignKey 13 | ALTER TABLE "_UserInterests" ADD CONSTRAINT "_UserInterests_A_fkey" FOREIGN KEY ("A") REFERENCES "CommunityCategory"("id") ON DELETE CASCADE ON UPDATE CASCADE; 14 | 15 | -- AddForeignKey 16 | ALTER TABLE "_UserInterests" ADD CONSTRAINT "_UserInterests_B_fkey" FOREIGN KEY ("B") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 17 | -------------------------------------------------------------------------------- /prisma/migrations/20250409101402_nerd_unique/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - A unique constraint covering the columns `[nerdAt]` on the table `user` will be added. If there are existing duplicate values, this will fail. 5 | 6 | */ 7 | -- CreateIndex 8 | CREATE UNIQUE INDEX "user_nerdAt_key" ON "user"("nerdAt"); 9 | -------------------------------------------------------------------------------- /prisma/migrations/20250409233203_add_original_post_id/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "posts" ADD COLUMN "originalPostId" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "posts" ADD CONSTRAINT "posts_originalPostId_fkey" FOREIGN KEY ("originalPostId") REFERENCES "posts"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /prisma/migrations/20250409235112_sharepost/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `originalPostId` on the `posts` table. All the data in the column will be lost. 5 | - You are about to drop the `Media` table. If the table is not empty, all the data it contains will be lost. 6 | 7 | */ 8 | -- DropForeignKey 9 | ALTER TABLE "Media" DROP CONSTRAINT "Media_postId_fkey"; 10 | 11 | -- DropForeignKey 12 | ALTER TABLE "posts" DROP CONSTRAINT "posts_originalPostId_fkey"; 13 | 14 | -- AlterTable 15 | ALTER TABLE "posts" DROP COLUMN "originalPostId", 16 | ADD COLUMN "sharedContent" TEXT, 17 | ADD COLUMN "sharedFromId" TEXT; 18 | 19 | -- DropTable 20 | DROP TABLE "Media"; 21 | 22 | -- CreateTable 23 | CREATE TABLE "media" ( 24 | "id" TEXT NOT NULL, 25 | "url" TEXT NOT NULL, 26 | "type" "MediaType" NOT NULL, 27 | "postId" TEXT NOT NULL, 28 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 29 | "sharedPostId" TEXT, 30 | 31 | CONSTRAINT "media_pkey" PRIMARY KEY ("id") 32 | ); 33 | 34 | -- CreateIndex 35 | CREATE UNIQUE INDEX "media_postId_sharedPostId_key" ON "media"("postId", "sharedPostId"); 36 | 37 | -- AddForeignKey 38 | ALTER TABLE "posts" ADD CONSTRAINT "posts_sharedFromId_fkey" FOREIGN KEY ("sharedFromId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE; 39 | 40 | -- AddForeignKey 41 | ALTER TABLE "media" ADD CONSTRAINT "media_post_media_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE CASCADE ON UPDATE CASCADE; 42 | 43 | -- AddForeignKey 44 | ALTER TABLE "media" ADD CONSTRAINT "media_shared_media_fkey" FOREIGN KEY ("sharedPostId") REFERENCES "posts"("id") ON DELETE SET NULL ON UPDATE CASCADE; 45 | 46 | -- AddForeignKey 47 | ALTER TABLE "media" ADD CONSTRAINT "media_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 48 | -------------------------------------------------------------------------------- /prisma/migrations/20250410005214_backup/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `sharedContent` on the `posts` table. All the data in the column will be lost. 5 | - You are about to drop the column `sharedFromId` on the `posts` table. All the data in the column will be lost. 6 | - You are about to drop the `media` table. If the table is not empty, all the data it contains will be lost. 7 | 8 | */ 9 | -- DropForeignKey 10 | ALTER TABLE "media" DROP CONSTRAINT "media_postId_fkey"; 11 | 12 | -- DropForeignKey 13 | ALTER TABLE "media" DROP CONSTRAINT "media_post_media_fkey"; 14 | 15 | -- DropForeignKey 16 | ALTER TABLE "media" DROP CONSTRAINT "media_shared_media_fkey"; 17 | 18 | -- DropForeignKey 19 | ALTER TABLE "posts" DROP CONSTRAINT "posts_sharedFromId_fkey"; 20 | 21 | -- AlterTable 22 | ALTER TABLE "posts" DROP COLUMN "sharedContent", 23 | DROP COLUMN "sharedFromId"; 24 | 25 | -- DropTable 26 | DROP TABLE "media"; 27 | 28 | -- CreateTable 29 | CREATE TABLE "Media" ( 30 | "id" TEXT NOT NULL, 31 | "url" TEXT NOT NULL, 32 | "type" "MediaType" NOT NULL, 33 | "postId" TEXT NOT NULL, 34 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 35 | 36 | CONSTRAINT "Media_pkey" PRIMARY KEY ("id") 37 | ); 38 | 39 | -- AddForeignKey 40 | ALTER TABLE "Media" ADD CONSTRAINT "Media_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE CASCADE ON UPDATE CASCADE; 41 | -------------------------------------------------------------------------------- /prisma/migrations/20250410014100_working/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropIndex 2 | DROP INDEX "user_nerdAt_key"; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20250411135652_add_shared_post_model/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "shared_posts" ( 3 | "id" TEXT NOT NULL, 4 | "postId" TEXT NOT NULL, 5 | "userId" TEXT NOT NULL, 6 | "sharedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 7 | 8 | CONSTRAINT "shared_posts_pkey" PRIMARY KEY ("id") 9 | ); 10 | 11 | -- AddForeignKey 12 | ALTER TABLE "shared_posts" ADD CONSTRAINT "shared_posts_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE CASCADE ON UPDATE CASCADE; 13 | 14 | -- AddForeignKey 15 | ALTER TABLE "shared_posts" ADD CONSTRAINT "shared_posts_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 16 | -------------------------------------------------------------------------------- /prisma/migrations/20250411140858_reverse/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the `shared_posts` table. If the table is not empty, all the data it contains will be lost. 5 | 6 | */ 7 | -- DropForeignKey 8 | ALTER TABLE "shared_posts" DROP CONSTRAINT "shared_posts_postId_fkey"; 9 | 10 | -- DropForeignKey 11 | ALTER TABLE "shared_posts" DROP CONSTRAINT "shared_posts_userId_fkey"; 12 | 13 | -- DropTable 14 | DROP TABLE "shared_posts"; 15 | -------------------------------------------------------------------------------- /prisma/migrations/20250411141355_add_additional_context_to_report/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "reports" ADD COLUMN "additionalContext" TEXT; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20250419062030_added_bug_report/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "bugReportSeverity" AS ENUM ('LOW', 'MEDIUM', 'HIGH'); 3 | 4 | -- CreateEnum 5 | CREATE TYPE "bugReportStatus" AS ENUM ('PENDING', 'RESOLVED', 'REJECTED'); 6 | 7 | -- CreateTable 8 | CREATE TABLE "bugReport" ( 9 | "id" TEXT NOT NULL, 10 | "userId" TEXT NOT NULL, 11 | "content" TEXT NOT NULL, 12 | "bugseverity" "bugReportSeverity" NOT NULL DEFAULT 'LOW', 13 | "status" "bugReportStatus" NOT NULL DEFAULT 'PENDING', 14 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 15 | "updatedAt" TIMESTAMP(3) NOT NULL, 16 | 17 | CONSTRAINT "bugReport_pkey" PRIMARY KEY ("id") 18 | ); 19 | 20 | -- AddForeignKey 21 | ALTER TABLE "bugReport" ADD CONSTRAINT "bugReport_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 22 | -------------------------------------------------------------------------------- /prisma/migrations/20250421124920_added_notification/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "notificationType" AS ENUM ('LIKE', 'COMMENT', 'FOLLOW'); 3 | 4 | -- CreateTable 5 | CREATE TABLE "notification" ( 6 | "id" TEXT NOT NULL, 7 | "userId" TEXT NOT NULL, 8 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 9 | "type" "notificationType" NOT NULL, 10 | "postId" TEXT, 11 | "commentId" TEXT, 12 | "followerId" TEXT, 13 | "updatedAt" TIMESTAMP(3) NOT NULL, 14 | 15 | CONSTRAINT "notification_pkey" PRIMARY KEY ("id") 16 | ); 17 | 18 | -- AddForeignKey 19 | ALTER TABLE "notification" ADD CONSTRAINT "notification_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 20 | 21 | -- AddForeignKey 22 | ALTER TABLE "notification" ADD CONSTRAINT "notification_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE CASCADE ON UPDATE CASCADE; 23 | 24 | -- AddForeignKey 25 | ALTER TABLE "notification" ADD CONSTRAINT "notification_commentId_fkey" FOREIGN KEY ("commentId") REFERENCES "postcomments"("id") ON DELETE CASCADE ON UPDATE CASCADE; 26 | 27 | -- AddForeignKey 28 | ALTER TABLE "notification" ADD CONSTRAINT "notification_followerId_fkey" FOREIGN KEY ("followerId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 29 | -------------------------------------------------------------------------------- /prisma/migrations/20250421153152_add_notifications/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the `notification` table. If the table is not empty, all the data it contains will be lost. 5 | 6 | */ 7 | -- CreateEnum 8 | CREATE TYPE "NotificationType" AS ENUM ('POST_LIKE', 'POST_COMMENT', 'NEW_FOLLOWER', 'PROJECT_UPDATE', 'PROJECT_STAR', 'COMMUNITY_INVITE'); 9 | 10 | -- DropForeignKey 11 | ALTER TABLE "notification" DROP CONSTRAINT "notification_commentId_fkey"; 12 | 13 | -- DropForeignKey 14 | ALTER TABLE "notification" DROP CONSTRAINT "notification_followerId_fkey"; 15 | 16 | -- DropForeignKey 17 | ALTER TABLE "notification" DROP CONSTRAINT "notification_postId_fkey"; 18 | 19 | -- DropForeignKey 20 | ALTER TABLE "notification" DROP CONSTRAINT "notification_userId_fkey"; 21 | 22 | -- DropTable 23 | DROP TABLE "notification"; 24 | 25 | -- DropEnum 26 | DROP TYPE "notificationType"; 27 | 28 | -- CreateTable 29 | CREATE TABLE "notifications" ( 30 | "id" TEXT NOT NULL, 31 | "userId" TEXT NOT NULL, 32 | "type" "NotificationType" NOT NULL, 33 | "read" BOOLEAN NOT NULL DEFAULT false, 34 | "postId" TEXT, 35 | "commentId" TEXT, 36 | "followerId" TEXT, 37 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 38 | "updatedAt" TIMESTAMP(3) NOT NULL, 39 | 40 | CONSTRAINT "notifications_pkey" PRIMARY KEY ("id") 41 | ); 42 | 43 | -- AddForeignKey 44 | ALTER TABLE "notifications" ADD CONSTRAINT "notifications_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 45 | -------------------------------------------------------------------------------- /prisma/migrations/20250422072157_updare/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the `notifications` table. If the table is not empty, all the data it contains will be lost. 5 | 6 | */ 7 | -- DropForeignKey 8 | ALTER TABLE "notifications" DROP CONSTRAINT "notifications_userId_fkey"; 9 | 10 | -- DropTable 11 | DROP TABLE "notifications"; 12 | 13 | -- DropEnum 14 | DROP TYPE "NotificationType"; 15 | -------------------------------------------------------------------------------- /prisma/migrations/20250422090303_notification/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "NotificationType" AS ENUM ('FOLLOW', 'POST_LIKE', 'POST_COMMENT', 'PROJECT_UPDATE', 'PROJECT_STAR', 'PROJECT_RATING', 'PROJECT_REVIEW', 'COMMUNITY_INVITE', 'COMMUNITY_POST', 'PROJECT_FOLLOW', 'MENTION'); 3 | 4 | -- CreateTable 5 | CREATE TABLE "notifications" ( 6 | "id" TEXT NOT NULL, 7 | "type" "NotificationType" NOT NULL, 8 | "userId" TEXT NOT NULL, 9 | "actorId" TEXT, 10 | "read" BOOLEAN NOT NULL DEFAULT false, 11 | "postId" TEXT, 12 | "commentId" TEXT, 13 | "projectId" TEXT, 14 | "communityId" TEXT, 15 | "message" TEXT NOT NULL, 16 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 17 | "updatedAt" TIMESTAMP(3) NOT NULL, 18 | 19 | CONSTRAINT "notifications_pkey" PRIMARY KEY ("id") 20 | ); 21 | 22 | -- CreateIndex 23 | CREATE INDEX "notifications_userId_read_createdAt_idx" ON "notifications"("userId", "read", "createdAt" DESC); 24 | 25 | -- AddForeignKey 26 | ALTER TABLE "notifications" ADD CONSTRAINT "notifications_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 27 | 28 | -- AddForeignKey 29 | ALTER TABLE "notifications" ADD CONSTRAINT "notifications_actorId_fkey" FOREIGN KEY ("actorId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE; 30 | 31 | -- AddForeignKey 32 | ALTER TABLE "notifications" ADD CONSTRAINT "notifications_postId_fkey" FOREIGN KEY ("postId") REFERENCES "posts"("id") ON DELETE CASCADE ON UPDATE CASCADE; 33 | 34 | -- AddForeignKey 35 | ALTER TABLE "notifications" ADD CONSTRAINT "notifications_commentId_fkey" FOREIGN KEY ("commentId") REFERENCES "postcomments"("id") ON DELETE CASCADE ON UPDATE CASCADE; 36 | 37 | -- AddForeignKey 38 | ALTER TABLE "notifications" ADD CONSTRAINT "notifications_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE; 39 | 40 | -- AddForeignKey 41 | ALTER TABLE "notifications" ADD CONSTRAINT "notifications_communityId_fkey" FOREIGN KEY ("communityId") REFERENCES "communities"("id") ON DELETE CASCADE ON UPDATE CASCADE; 42 | -------------------------------------------------------------------------------- /prisma/migrations/20250422093720_update_nerdspace/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "update" ( 3 | "id" TEXT NOT NULL, 4 | "title" TEXT NOT NULL, 5 | "content" TEXT NOT NULL, 6 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 7 | "updatedAt" TIMESTAMP(3) NOT NULL, 8 | 9 | CONSTRAINT "update_pkey" PRIMARY KEY ("id") 10 | ); 11 | -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (e.g., Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /public/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/01.png -------------------------------------------------------------------------------- /public/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/03.png -------------------------------------------------------------------------------- /public/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/04.png -------------------------------------------------------------------------------- /public/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/05.png -------------------------------------------------------------------------------- /public/Elon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/Elon.png -------------------------------------------------------------------------------- /public/Enistine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/Enistine.png -------------------------------------------------------------------------------- /public/adawa.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/adawa.webp -------------------------------------------------------------------------------- /public/art.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/art.jpg -------------------------------------------------------------------------------- /public/auth-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/auth-bg.png -------------------------------------------------------------------------------- /public/bg9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/bg9.jpg -------------------------------------------------------------------------------- /public/build.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/build.jpg -------------------------------------------------------------------------------- /public/fashion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/fashion.jpg -------------------------------------------------------------------------------- /public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/fonts/ITCGaramondStd-BkCond.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/fonts/ITCGaramondStd-BkCond.ttf -------------------------------------------------------------------------------- /public/fonts/ITCGaramondStd-LtCond.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/fonts/ITCGaramondStd-LtCond.ttf -------------------------------------------------------------------------------- /public/fonts/ITCGaramondStd-LtCondIta.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/fonts/ITCGaramondStd-LtCondIta.ttf -------------------------------------------------------------------------------- /public/fonts/user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/fonts/user.jpg -------------------------------------------------------------------------------- /public/game.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/game.jpg -------------------------------------------------------------------------------- /public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/gwax.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/gwax.jpg -------------------------------------------------------------------------------- /public/hu.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/hu.webp -------------------------------------------------------------------------------- /public/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/logo.jpg -------------------------------------------------------------------------------- /public/music.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/music.jpg -------------------------------------------------------------------------------- /public/nerd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/nerd.jpg -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/obsession.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/obsession.jpg -------------------------------------------------------------------------------- /public/robot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/robot.jpg -------------------------------------------------------------------------------- /public/user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/user.jpg -------------------------------------------------------------------------------- /public/user_placeholder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/public/user_placeholder.jpg -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/(app)/ai/layout.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const ProfileLayout = ({ children }: { children: React.ReactNode }) => { 4 | return <>{children}; 5 | }; 6 | 7 | export default ProfileLayout; 8 | -------------------------------------------------------------------------------- /src/app/(app)/ai/page.tsx: -------------------------------------------------------------------------------- 1 | import LeftNavbar from "@/components/navbar/left-navbar"; 2 | import MobileNavBar from "@/components/navbar/mobile-nav-bar"; 3 | import SoonComponent from "@/components/soon/soon"; 4 | 5 | const Project = () => { 6 | return ( 7 |
8 | 9 |
10 | 11 |
12 | 13 |
14 | ); 15 | }; 16 | 17 | export default Project; 18 | -------------------------------------------------------------------------------- /src/app/(app)/community/layout.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const ProfileLayout = ({ children }: { children: React.ReactNode }) => { 4 | return <>{children}; 5 | }; 6 | 7 | export default ProfileLayout; 8 | -------------------------------------------------------------------------------- /src/app/(app)/community/page.tsx: -------------------------------------------------------------------------------- 1 | import LeftNavbar from "@/components/navbar/left-navbar"; 2 | import MobileNavBar from "@/components/navbar/mobile-nav-bar"; 3 | import SoonComponent from "@/components/soon/soon"; 4 | import React from "react"; 5 | 6 | const Project = () => { 7 | return ( 8 |
9 | 10 |
11 | 12 |
13 | 14 | 15 |
16 | ); 17 | }; 18 | 19 | export default Project; 20 | -------------------------------------------------------------------------------- /src/app/(app)/event/layout.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const ProfileLayout = ({ children }: { children: React.ReactNode }) => { 4 | return <>{children}; 5 | }; 6 | 7 | export default ProfileLayout; 8 | -------------------------------------------------------------------------------- /src/app/(app)/event/page.tsx: -------------------------------------------------------------------------------- 1 | import LeftNavbar from "@/components/navbar/left-navbar"; 2 | import MobileNavBar from "@/components/navbar/mobile-nav-bar"; 3 | import SoonComponent from "@/components/soon/soon"; 4 | 5 | const Project = () => { 6 | return ( 7 |
8 | 9 |
10 | 11 |
12 | 13 | 14 |
15 | ); 16 | }; 17 | 18 | export default Project; 19 | -------------------------------------------------------------------------------- /src/app/(app)/explore/layout.tsx: -------------------------------------------------------------------------------- 1 | const ProfileLayout = ({ children }: { children: React.ReactNode }) => { 2 | return <>{children}; 3 | }; 4 | 5 | export default ProfileLayout; 6 | -------------------------------------------------------------------------------- /src/app/(app)/explore/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import ExploreEntry from "@/components/explore/explore-entry"; 4 | import LeftNavbar from "@/components/navbar/left-navbar"; 5 | import MobileNavBar from "@/components/navbar/mobile-nav-bar"; 6 | import { useSearchParams } from "next/navigation"; 7 | import { useEffect, Suspense } from "react"; 8 | import useSearchStore from "@/store/search.store"; 9 | 10 | const ExploreContent = () => { 11 | const searchParams = useSearchParams(); 12 | const { setQuery } = useSearchStore(); 13 | 14 | useEffect(() => { 15 | if (searchParams) { 16 | const query = searchParams.get("q"); 17 | if (query) { 18 | setQuery(query); 19 | } 20 | } 21 | }, [searchParams, setQuery]); 22 | 23 | return ( 24 |
25 |
26 |
27 | 28 |
29 | 30 |
31 |
32 |
33 | 34 |
35 | ); 36 | }; 37 | 38 | const Explore = () => { 39 | return ( 40 | Loading...}> 41 | 42 | 43 | ); 44 | }; 45 | 46 | export default Explore; 47 | -------------------------------------------------------------------------------- /src/app/(app)/layout.tsx: -------------------------------------------------------------------------------- 1 | import Navbar from "@/components/navbar"; 2 | import React from "react"; 3 | import { PostHogProvider } from "@/lib/posthog-provider"; 4 | 5 | const AppLayout = ({ children }: { children: React.ReactNode }) => { 6 | return ( 7 | 8 |
9 | {/*
13 |
17 |
*/} 21 | 22 |
{children}
23 |
24 | 25 | ); 26 | }; 27 | 28 | export default AppLayout; 29 | -------------------------------------------------------------------------------- /src/app/(app)/page.tsx: -------------------------------------------------------------------------------- 1 | import LeftNavbar from "@/components/navbar/left-navbar"; 2 | import MobileNavBar from "@/components/navbar/mobile-nav-bar"; 3 | import RightNavbar from "@/components/navbar/right-navbar"; 4 | import PostInput from "@/components/post/post-input"; 5 | import RenderPOst from "@/components/post/render-post"; 6 | 7 | export default function Home() { 8 | return ( 9 |
10 | 11 |
12 | 13 | 14 |
15 | 16 | 17 |
18 | ); 19 | } 20 | // console.log the current url -------------------------------------------------------------------------------- /src/app/(app)/profile/[userId]/page.tsx: -------------------------------------------------------------------------------- 1 | // This is a Next.js server component for profile pages 2 | "use client"; 3 | 4 | import { use } from "react"; 5 | 6 | export default function ProfilePage({ 7 | params, 8 | }: { 9 | params: Promise<{ userId: string }>; 10 | }) { 11 | const { userId } = use(params); 12 | 13 | return ( 14 |
15 |

Profile {userId}

16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/app/(app)/profile/layout.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const ProfileLayout = ({ children }: { children: React.ReactNode }) => { 4 | return <>{children}; 5 | }; 6 | 7 | export default ProfileLayout; 8 | -------------------------------------------------------------------------------- /src/app/(app)/profile/page.tsx: -------------------------------------------------------------------------------- 1 | import LeftNavbar from "@/components/navbar/left-navbar"; 2 | import MobileNavBar from "@/components/navbar/mobile-nav-bar"; 3 | import ProfilePage from "@/components/settings/profile"; 4 | 5 | export default function Profile() { 6 | return ( 7 |
8 | 9 |
10 | 11 |
12 | 13 | 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/app/(app)/profile/user/[userId]/followers/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import LeftNavbar from "@/components/navbar/left-navbar"; 4 | import MobileNavBar from "@/components/navbar/mobile-nav-bar"; 5 | import FollowersFollowingList from "@/components/settings/followers-following-list"; 6 | import { use } from "react"; 7 | 8 | // 9 | export default function FollowersPage({ 10 | params, 11 | }: { 12 | params: Promise<{ userId: string }>; 13 | }) { 14 | const { userId } = use(params); 15 | 16 | return ( 17 |
18 | 19 |
20 |
21 |

Followers

22 | 23 |
24 |
25 | 26 | 27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/app/(app)/profile/user/[userId]/following/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import LeftNavbar from "@/components/navbar/left-navbar"; 4 | import MobileNavBar from "@/components/navbar/mobile-nav-bar"; 5 | import FollowersFollowingList from "@/components/settings/followers-following-list"; 6 | import { use } from "react"; 7 | 8 | export default function FollowingPage({ 9 | params, 10 | }: { 11 | params: Promise<{ userId: string }>; 12 | }) { 13 | const { userId } = use(params); 14 | 15 | return ( 16 |
17 | 18 |
19 |
20 |

Following

21 | 22 |
23 |
24 | 25 | 26 |
27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /src/app/(app)/project/[id]/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import LeftNavbar from "@/components/navbar/left-navbar"; 4 | import { use } from "react"; 5 | import ProjectDetail from "@/components/project/project-detail"; 6 | 7 | export default function ProjectDetailPage({ 8 | params, 9 | }: { 10 | params: Promise<{ id: string }>; 11 | }) { 12 | const { id } = use(params); 13 | 14 | return ( 15 |
16 | 17 |
18 | 19 |
20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /src/app/(app)/project/layout.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const ProfileLayout = ({ children }: { children: React.ReactNode }) => { 4 | return <>{children}; 5 | }; 6 | 7 | export default ProfileLayout; 8 | -------------------------------------------------------------------------------- /src/app/(app)/project/page.tsx: -------------------------------------------------------------------------------- 1 | import LeftNavbar from "@/components/navbar/left-navbar"; 2 | import MobileNavBar from "@/components/navbar/mobile-nav-bar"; 3 | import ProjectsPage from "@/components/project/project-component"; 4 | 5 | const Project = () => { 6 | return ( 7 |
8 |
9 |
10 | 11 |
12 | 13 |
14 |
15 |
16 | 17 |
18 | ); 19 | }; 20 | 21 | export default Project; 22 | -------------------------------------------------------------------------------- /src/app/(app)/sandbox/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { FileUploader } from "@/components/media/media-uploader"; 4 | import React from "react"; 5 | 6 | const SandBox = () => { 7 | return ( 8 |
9 |
10 | { 14 | console.log("Selected files:", files); 15 | 16 | 17 | }} 18 | /> 19 |
20 |
21 | ); 22 | }; 23 | 24 | export default SandBox; 25 | -------------------------------------------------------------------------------- /src/app/(app)/user-profile/layout.tsx: -------------------------------------------------------------------------------- 1 | import MobileNavBar from "@/components/navbar/mobile-nav-bar"; 2 | import LeftNavbar from "@/components/navbar/left-navbar"; 3 | import React from "react"; 4 | 5 | const ProfileLayout = ({ children }: { children: React.ReactNode }) => { 6 | return ( 7 |
8 | 9 | {children} 10 | 11 |
12 | ); 13 | }; 14 | 15 | export default ProfileLayout; 16 | -------------------------------------------------------------------------------- /src/app/(auth)/layout.tsx: -------------------------------------------------------------------------------- 1 | import { authClient } from "@/lib/auth-client"; 2 | import { redirect } from "next/navigation"; 3 | 4 | const AuthLayout = async ({ children }: { children: React.ReactNode }) => { 5 | const session = await authClient.getSession(); 6 | 7 | if (session.data?.user) { 8 | redirect("/"); 9 | } 10 | 11 | return
{children}
; 12 | }; 13 | 14 | export default AuthLayout; 15 | -------------------------------------------------------------------------------- /src/app/(auth)/reset-password/page.tsx: -------------------------------------------------------------------------------- 1 | import { GalleryVerticalEnd } from "lucide-react"; 2 | 3 | import { ResetPasswordFrom } from "@/components/reset-password"; 4 | import Image from "next/image"; 5 | import { Suspense } from "react"; 6 | 7 | export default function ResetPasswordPage() { 8 | return ( 9 |
10 |
11 |
12 |
13 |
14 | 15 |
16 | Loading...
}> 17 | 18 | 19 |
20 |
21 |
22 |
23 |
24 |

25 | Start your journey here 26 |

27 | @ 28 |

29 | Build one simple profile and let our AI work it's magic. 30 | We'll automatically apply to hundreds of jobs for you. Focus on 31 | what matters most - your skills and experience. 32 |

33 | nerd 40 |

41 | Yeabsra Ashebir 42 |

43 |

Developer

44 |
45 |
46 |
47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /src/app/(auth)/signup/page.tsx: -------------------------------------------------------------------------------- 1 | import { GalleryVerticalEnd } from "lucide-react"; 2 | 3 | import { SignUpForm } from "@/components/signup-form"; 4 | import Image from "next/image"; 5 | 6 | export default function SignUpPage() { 7 | return ( 8 |
9 |
10 |
11 |
12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 |
20 |
21 |

22 | Start your{" "} 23 | Journey{" "} 24 | here 25 |

26 | 27 |

28 | Create a simple profile and connect with like-minded people who 29 | share your passions. Explore communities, collaborate on projects, 30 | and grow with others who truly get you. 31 |

32 | 33 | nerd 40 | 41 |

42 | Yeabsra Ashebir 43 |

44 |

Developer

45 |
46 |
47 |
48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /src/app/api/auth/[...all]/route.ts: -------------------------------------------------------------------------------- 1 | import { auth } from "@/lib/auth"; // path to your auth file 2 | import { toNextJsHandler } from "better-auth/next-js"; 3 | 4 | export const { POST, GET } = toNextJsHandler(auth); 5 | -------------------------------------------------------------------------------- /src/app/api/dummy/route.ts: -------------------------------------------------------------------------------- 1 | import { generateUsers } from "@/script/dummy-users"; 2 | import { NextRequest, NextResponse } from "next/server"; 3 | 4 | const users = generateUsers(100); 5 | 6 | export const GET = async (request: NextRequest) => { 7 | try { 8 | const { searchParams } = new URL(request.url); 9 | const page = parseInt(searchParams.get("page") || "1"); 10 | const pageSize = parseInt(searchParams.get("pageSize") || "10"); 11 | 12 | const start = (page - 1) * pageSize; 13 | const end = start + pageSize; 14 | 15 | const data = users.slice(start, end); 16 | 17 | return NextResponse.json( 18 | { 19 | data: data, 20 | totalPage: Math.ceil(users.length / pageSize), 21 | previouspage: page - 1, 22 | nextpage: page + 1, 23 | }, 24 | { status: 200 }, 25 | ); 26 | } catch (error) { 27 | return NextResponse.json({ error: error }, { status: 500 }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /src/app/api/media-upload/route.ts: -------------------------------------------------------------------------------- 1 | import getUserSession from "@/functions/get-user"; 2 | import { prisma } from "@/lib/prisma"; 3 | import { NextRequest, NextResponse } from "next/server"; 4 | import { z } from "zod"; 5 | 6 | const MediaSchema = z.object({ 7 | postId: z.string(), 8 | url: z.string().url(), 9 | mediaType: z.enum(["IMAGE", "VIDEO", "GIF"]), 10 | }); 11 | 12 | type MediaType = z.infer; 13 | 14 | export const POST = async (req: NextRequest) => { 15 | try { 16 | const session = await getUserSession(); 17 | const body: MediaType = await req.json(); 18 | 19 | if (!session) { 20 | return NextResponse.json( 21 | { message: "unauthorized | not logged in" }, 22 | { status: 400 }, 23 | ); 24 | } 25 | 26 | const validationValue = MediaSchema.safeParse(body); 27 | if (!validationValue.success || validationValue.error) { 28 | return NextResponse.json( 29 | { message: "validation error", error: validationValue.error.message }, 30 | { status: 401 }, 31 | ); 32 | } 33 | 34 | const newMedia = await prisma.media.create({ 35 | data: { 36 | postId: body.postId, 37 | url: body.url, 38 | type: body.mediaType, 39 | }, 40 | }); 41 | 42 | return NextResponse.json( 43 | { 44 | data: newMedia, 45 | message: "Media uploaded successfully", 46 | }, 47 | { status: 201 }, 48 | ); 49 | } catch (e) { 50 | return NextResponse.json({ error: e }, { status: 500 }); 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /src/app/api/post/[id]/route.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from "@/lib/prisma"; 2 | import { NextRequest, NextResponse } from "next/server"; 3 | 4 | export const GET = async (request: NextRequest) => { 5 | try { 6 | const id = request.nextUrl.pathname.split("/").pop() as string; 7 | 8 | const post = await prisma.post.findUnique({ 9 | where: { id }, 10 | include: { 11 | user: { 12 | include: { 13 | following: true, 14 | }, 15 | }, 16 | likes: true, 17 | bookmarks: true, 18 | media: true, 19 | postcomments: { 20 | include: { 21 | user: true, 22 | replies: { 23 | include: { 24 | user: true, 25 | }, 26 | }, 27 | }, 28 | }, 29 | project: { 30 | include: { 31 | _count: true, 32 | }, 33 | }, 34 | _count: { 35 | select: { 36 | likes: true, 37 | bookmarks: true, 38 | postcomments: true, 39 | }, 40 | }, 41 | }, 42 | }); 43 | 44 | if (!post) { 45 | return NextResponse.json({ message: "Post not found" }, { status: 404 }); 46 | } 47 | 48 | const modifiedPost = { 49 | ...post, 50 | user: { 51 | ...post.user, 52 | isFollowingAuthor: post.user.following.length > 0, 53 | }, 54 | }; 55 | 56 | return NextResponse.json( 57 | { 58 | data: modifiedPost, 59 | message: "Post fetched successfully", 60 | }, 61 | { status: 200 }, 62 | ); 63 | } catch (error) { 64 | console.error("Error fetching post:", error); 65 | return NextResponse.json( 66 | { error: "Internal server error" }, 67 | { status: 500 }, 68 | ); 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /src/app/api/post/bookmark/route.ts: -------------------------------------------------------------------------------- 1 | import getUserSession from "@/functions/get-user"; 2 | import { prisma } from "@/lib/prisma"; 3 | import bookmarkSchema from "@/validation/bookmark.validation"; 4 | import { NextResponse } from "next/server"; 5 | 6 | export const POST = async (req: Request) => { 7 | try { 8 | const session = await getUserSession(); 9 | 10 | if (!session) { 11 | return NextResponse.json( 12 | { 13 | message: "unauthorized | not logged in", 14 | }, 15 | { status: 400 }, 16 | ); 17 | } 18 | 19 | const body = await req.json(); 20 | const result = bookmarkSchema.safeParse(body); 21 | 22 | if (!result.success) { 23 | return NextResponse.json( 24 | { 25 | message: "Invalid request body", 26 | errors: result.error.errors, 27 | }, 28 | { status: 400 }, 29 | ); 30 | } 31 | 32 | const { userId, postId } = result.data; 33 | 34 | const existingBookmark = await prisma.bookmark.findFirst({ 35 | where: { userId, postId }, 36 | }); 37 | 38 | if (existingBookmark) { 39 | await prisma.bookmark.delete({ where: { id: existingBookmark.id } }); 40 | return NextResponse.json( 41 | { message: "Bookmark removed" }, 42 | { status: 200 }, 43 | ); 44 | } 45 | 46 | await prisma.bookmark.create({ 47 | data: { userId, postId }, 48 | }); 49 | 50 | return NextResponse.json({ message: "Bookmarked post" }, { status: 201 }); 51 | } catch (err) { 52 | console.error("Error updating comment:", err); 53 | return NextResponse.json({ error: "error" }, { status: 500 }); 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /src/app/api/project/[id]/updates/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from "next/server"; 2 | import { prisma } from "@/lib/prisma"; 3 | import getUserSession from "@/functions/get-user"; 4 | 5 | export async function GET(req: NextRequest) { 6 | try { 7 | const session = await getUserSession(); 8 | if (!session) { 9 | return NextResponse.json( 10 | { message: "Unauthorized | Not logged in" }, 11 | { status: 401 }, 12 | ); 13 | } 14 | 15 | const url = new URL(req.url); 16 | const pathParts = url.pathname.split("/").filter((part) => part !== ""); 17 | const id = pathParts[pathParts.length - 1]; // Assuming the last part is the id 18 | 19 | const cursor = req.nextUrl.searchParams.get("cursor"); 20 | const limit = parseInt(req.nextUrl.searchParams.get("limit") || "3", 10); // Default to 3 updates per page 21 | 22 | const updates = await prisma.projectUpdate.findMany({ 23 | where: { projectId: id }, 24 | take: limit, 25 | skip: cursor ? 1 : 0, 26 | cursor: cursor ? { id: cursor } : undefined, 27 | orderBy: { createdAt: "desc" }, 28 | include: { likes: true, comments: true }, 29 | }); 30 | 31 | const nextCursor = 32 | updates.length === limit ? updates[updates.length - 1].id : null; 33 | 34 | return NextResponse.json( 35 | { 36 | data: updates, 37 | nextCursor, // Include nextCursor in the response 38 | }, 39 | { status: 200 }, 40 | ); 41 | } catch (error: any) { 42 | console.error("Error fetching project updates:", error); 43 | return NextResponse.json( 44 | { message: "Internal server error" }, 45 | { status: 500 }, 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/app/api/project/rank/route.ts: -------------------------------------------------------------------------------- 1 | import getUserSession from "@/functions/get-user"; 2 | import { prisma } from "@/lib/prisma"; 3 | import { NextRequest, NextResponse } from "next/server"; 4 | 5 | export const GET = async (req: NextRequest) => { 6 | try { 7 | const session = await getUserSession(); 8 | if (!session) { 9 | return NextResponse.json( 10 | { message: "unauthorized | not logged in" }, 11 | { status: 400 }, 12 | ); 13 | } 14 | 15 | const limit = parseInt(req.nextUrl.searchParams.get("limit") || "5", 10); 16 | const cursor = req.nextUrl.searchParams.get("cursor"); 17 | 18 | const projects = await prisma.project.findMany({ 19 | take: limit, 20 | skip: cursor ? 1 : 0, 21 | cursor: cursor ? { id: cursor } : undefined, 22 | orderBy: { 23 | stars: { 24 | _count: "desc", 25 | }, 26 | }, 27 | include: { 28 | _count: { 29 | select: { 30 | stars: true, 31 | followers: true, 32 | updates: true, 33 | reviews: true, 34 | }, 35 | }, 36 | }, 37 | }); 38 | 39 | const nextCursor = 40 | projects.length === limit ? projects[projects.length - 1].id : null; 41 | 42 | return NextResponse.json({ 43 | projects: projects.map((project) => ({ 44 | id: project.id, 45 | name: project.name, 46 | description: project.description, 47 | stars: project._count.stars, 48 | image: project.image, 49 | follower: project._count.followers, 50 | review: project._count.reviews, 51 | update: project._count.updates, 52 | })), 53 | nextCursor, 54 | }); 55 | } catch (error) { 56 | console.error("Error fetching project rankings:", error); 57 | return NextResponse.json({ error: "error" }, { status: 500 }); 58 | } 59 | }; 60 | -------------------------------------------------------------------------------- /src/app/api/project/recommendation/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | import { prisma } from "@/lib/prisma"; 3 | import getUserSession from "@/functions/get-user"; 4 | 5 | export async function GET() { 6 | try { 7 | const session = await getUserSession(); 8 | if (!session) { 9 | return NextResponse.json( 10 | { message: "unauthorized | not logged in" }, 11 | { status: 401 }, 12 | ); 13 | } 14 | 15 | const projects = await prisma.project.findMany({ 16 | where: { 17 | AND: [ 18 | { 19 | userId: { 20 | not: session.user.id, 21 | }, 22 | }, 23 | { 24 | followers: { 25 | none: { 26 | userId: session.user.id, 27 | }, 28 | }, 29 | }, 30 | ], 31 | }, 32 | take: 8, 33 | orderBy: { createdAt: "desc" }, 34 | include: { 35 | followers: true, 36 | }, 37 | }); 38 | 39 | const transformedProjects = projects.map((project) => ({ 40 | id: project.id, 41 | name: project.name, 42 | description: project.description, 43 | image: project.image || "/placeholder-image.jpg", 44 | status: project.status, 45 | category: project.category || [], 46 | members: project.followers.length, 47 | })); 48 | 49 | return NextResponse.json({ projects: transformedProjects }); 50 | } catch (error) { 51 | console.error("Error fetching projects:", error); 52 | return NextResponse.json( 53 | { error: "Failed to fetch projects" }, 54 | { status: 500 }, 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/app/api/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | 3 | export async function GET() { 4 | return NextResponse.json({ message: "Hello from the API!" }); 5 | } 6 | -------------------------------------------------------------------------------- /src/app/api/security/route.ts: -------------------------------------------------------------------------------- 1 | import getUserSession from "@/functions/get-user"; 2 | import { prisma } from "@/lib/prisma"; 3 | import { NextRequest } from "next/server"; 4 | 5 | export const PATCH = async (req: NextRequest) => { 6 | try { 7 | const { id }: { id: string } = await req.json(); 8 | const session = await getUserSession(); 9 | 10 | if (!session) { 11 | return Response.json( 12 | { 13 | message: "unauthorized | not logged in", 14 | }, 15 | { status: 400 }, 16 | ); 17 | } 18 | 19 | const post = await prisma.post.findUnique({ 20 | where: { 21 | id: id, 22 | userId: session.user.id, 23 | }, 24 | }); 25 | 26 | if (!post) { 27 | return Response.json( 28 | { 29 | message: "Post not found or you do not have access to it", 30 | }, 31 | { status: 404 }, 32 | ); 33 | } 34 | 35 | const updatedAccess = await prisma.post.update({ 36 | where: { 37 | id: id, 38 | }, 39 | data: { 40 | access: post.access === "public" ? "private" : "public", 41 | }, 42 | }); 43 | 44 | return Response.json(updatedAccess, { status: 200 }); 45 | } catch (error) { 46 | return Response.json( 47 | { 48 | message: "An error occurred", 49 | }, 50 | { status: 500 }, 51 | ); 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /src/app/api/user/route.ts: -------------------------------------------------------------------------------- 1 | import getUserSession from "@/functions/get-user"; 2 | import { prisma } from "@/lib/prisma"; 3 | import { NextRequest, NextResponse } from "next/server"; 4 | 5 | export const GET = async (req: NextRequest) => { 6 | try { 7 | const session = await getUserSession(); 8 | const userId = await req.nextUrl.searchParams.get("userId"); 9 | 10 | if (!userId) { 11 | return NextResponse.json( 12 | { 13 | message: "userId is required", 14 | }, 15 | { status: 400 }, 16 | ); 17 | } 18 | 19 | if (!session) { 20 | return Response.json( 21 | { 22 | message: "unauthorized | not logged in", 23 | }, 24 | { status: 400 }, 25 | ); 26 | } 27 | 28 | const user = await prisma.user.findUnique({ 29 | where: { 30 | id: userId, 31 | }, 32 | 33 | include: { 34 | posts: true, 35 | country: true, 36 | followers: true, 37 | following: true, 38 | }, 39 | }); 40 | 41 | if (!user) { 42 | return NextResponse.json( 43 | { 44 | message: "User not found", 45 | }, 46 | { status: 404 }, 47 | ); 48 | } 49 | 50 | return NextResponse.json({ 51 | data: user, 52 | }); 53 | } catch (error) { 54 | console.error("Error fetching posts:", error); 55 | return NextResponse.json({ error: "error" }, { status: 500 }); 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /src/app/api/users/[userId]/counts/route.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from "@/lib/prisma"; 2 | import { NextRequest, NextResponse } from "next/server"; 3 | 4 | export async function GET(request: NextRequest) { 5 | try { 6 | const url = new URL(request.url); 7 | const pathParts = url.pathname.split("/").filter((part) => part !== ""); 8 | const userId = pathParts[pathParts.length - 1]; 9 | 10 | console.log("Fetching counts for userId:", userId); 11 | 12 | if (!userId) { 13 | return NextResponse.json( 14 | { 15 | message: "User ID is required", 16 | }, 17 | { status: 400 }, 18 | ); 19 | } 20 | 21 | // Debug: Check all follows for this user 22 | const allFollows = await prisma.follows.findMany({ 23 | where: { 24 | OR: [{ followerId: userId }, { followingId: userId }], 25 | }, 26 | }); 27 | console.log("All follows for user:", allFollows); 28 | 29 | const [followersCount, followingCount] = await Promise.all([ 30 | prisma.follows.count({ 31 | where: { 32 | followingId: userId, 33 | }, 34 | }), 35 | prisma.follows.count({ 36 | where: { 37 | followerId: userId, 38 | }, 39 | }), 40 | ]); 41 | 42 | console.log("Counts result:", { followersCount, followingCount }); 43 | 44 | return NextResponse.json({ 45 | followers: followersCount, 46 | following: followingCount, 47 | }); 48 | } catch (error) { 49 | console.error("Error fetching follow counts:", error); 50 | return NextResponse.json( 51 | { error: "Failed to fetch follow counts" }, 52 | { status: 500 }, 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/app/api/users/[userId]/route.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from "@/lib/prisma"; 2 | import { NextRequest, NextResponse } from "next/server"; 3 | 4 | export async function GET(request: NextRequest) { 5 | try { 6 | const url = new URL(request.url); 7 | const pathParts = url.pathname.split("/").filter((part) => part !== ""); 8 | const userId = pathParts[pathParts.length - 1]; 9 | 10 | if (!userId) { 11 | return NextResponse.json( 12 | { 13 | message: "User ID is required", 14 | }, 15 | { status: 400 }, 16 | ); 17 | } 18 | 19 | const user = await prisma.user.findUnique({ 20 | where: { id: userId }, 21 | include: { 22 | _count: { 23 | select: { 24 | followers: true, 25 | following: true, 26 | }, 27 | }, 28 | country: true, 29 | }, 30 | }); 31 | 32 | if (!user) { 33 | return NextResponse.json( 34 | { 35 | message: "User not found", 36 | }, 37 | { status: 404 }, 38 | ); 39 | } 40 | 41 | return NextResponse.json({ 42 | data: user, 43 | }); 44 | } catch (error) { 45 | console.error("Error fetching user:", error); 46 | return NextResponse.json( 47 | { error: "Failed to fetch user" }, 48 | { status: 500 }, 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/app/api/users/check-follow/route.ts: -------------------------------------------------------------------------------- 1 | import getUserSession from "@/functions/get-user"; 2 | import { prisma } from "@/lib/prisma"; 3 | import { NextResponse } from "next/server"; 4 | 5 | export async function GET(request: Request) { 6 | try { 7 | const session = await getUserSession(); 8 | if (!session?.user) { 9 | return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); 10 | } 11 | 12 | const { searchParams } = new URL(request.url); 13 | const userIds = searchParams.get("userIds")?.split(","); 14 | 15 | if (!userIds || userIds.length === 0) { 16 | return NextResponse.json( 17 | { error: "User IDs are required" }, 18 | { status: 400 }, 19 | ); 20 | } 21 | 22 | const follows = await prisma.follows.findMany({ 23 | where: { 24 | followerId: session.user.id, 25 | followingId: { 26 | in: userIds, 27 | }, 28 | }, 29 | select: { 30 | followingId: true, 31 | }, 32 | }); 33 | 34 | const followStatus = userIds.reduce( 35 | (acc, userId) => { 36 | acc[userId] = follows.some((follow) => follow.followingId === userId); 37 | return acc; 38 | }, 39 | {} as Record, 40 | ); 41 | 42 | return NextResponse.json(followStatus); 43 | } catch (error) { 44 | console.error("Error checking follow status:", error); 45 | return NextResponse.json( 46 | { error: "Internal server error" }, 47 | { status: 500 }, 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/app/api/users/projects/owned/route.ts: -------------------------------------------------------------------------------- 1 | import getUserSession from "@/functions/get-user"; 2 | import { prisma } from "@/lib/prisma"; 3 | import { NextRequest, NextResponse } from "next/server"; 4 | 5 | export async function GET(request: NextRequest) { 6 | try { 7 | const session = await getUserSession(); 8 | const cursor = request.nextUrl.searchParams.get("cursor"); 9 | const limit = parseInt(request.nextUrl.searchParams.get("limit") || "10"); 10 | 11 | if (!session) { 12 | return NextResponse.json( 13 | { 14 | message: "unauthorized | not logged in", 15 | }, 16 | { status: 400 }, 17 | ); 18 | } 19 | 20 | const [ownedProjects, total] = await Promise.all([ 21 | prisma.project.findMany({ 22 | where: { 23 | userId: session.user.id, 24 | ...(cursor && { 25 | id: { 26 | lt: cursor, 27 | }, 28 | }), 29 | }, 30 | include: { 31 | user: { 32 | select: { 33 | id: true, 34 | name: true, 35 | image: true, 36 | }, 37 | }, 38 | }, 39 | orderBy: { 40 | id: "desc", 41 | }, 42 | take: limit + 1, 43 | }), 44 | prisma.project.count({ 45 | where: { 46 | userId: session.user.id, 47 | }, 48 | }), 49 | ]); 50 | 51 | const hasNextPage = ownedProjects.length > limit; 52 | const items = hasNextPage ? ownedProjects.slice(0, -1) : ownedProjects; 53 | const nextCursor = hasNextPage ? items[items.length - 1].id : null; 54 | 55 | return NextResponse.json({ 56 | data: items, 57 | pagination: { 58 | nextCursor, 59 | hasNextPage, 60 | total, 61 | }, 62 | }); 63 | } catch (error) { 64 | console.error("Error fetching owned projects:", error); 65 | return NextResponse.json( 66 | { error: "Failed to fetch owned projects" }, 67 | { status: 500 }, 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/app/api/users/route.ts: -------------------------------------------------------------------------------- 1 | import getUserSession from "@/functions/get-user"; 2 | import { prisma } from "@/lib/prisma"; 3 | import { NextResponse } from "next/server"; 4 | 5 | export async function GET(request: Request) { 6 | try { 7 | const session = await getUserSession(); 8 | 9 | if (!session) { 10 | return NextResponse.json( 11 | { 12 | message: "unauthorized | not logged in", 13 | }, 14 | { status: 400 }, 15 | ); 16 | } 17 | const { searchParams } = new URL(request.url); 18 | const exclude = searchParams.get("exclude"); 19 | const limit = parseInt(searchParams.get("limit") || "5"); 20 | 21 | const users = await prisma.user.findMany({ 22 | where: exclude 23 | ? { 24 | NOT: { id: exclude }, 25 | } 26 | : undefined, 27 | take: limit, 28 | }); 29 | 30 | return NextResponse.json(users); 31 | } catch (error) { 32 | console.error("Error fetching users:", error); 33 | return NextResponse.json( 34 | { error: "Failed to fetch users" }, 35 | { status: 500 }, 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app/api/whoami/post/route.ts: -------------------------------------------------------------------------------- 1 | import getUserSession from "@/functions/get-user"; 2 | import { prisma } from "@/lib/prisma"; 3 | import { NextRequest, NextResponse } from "next/server"; 4 | 5 | export async function GET(request: NextRequest) { 6 | try { 7 | const session = await getUserSession(); 8 | 9 | if (!session) { 10 | return Response.json( 11 | { 12 | message: "unauthorized | not logged in", 13 | }, 14 | { status: 400 }, 15 | ); 16 | } 17 | const cursor = request.nextUrl.searchParams.get("cursor") || null; 18 | const limit = 5; 19 | 20 | const posts = await prisma.post.findMany({ 21 | where: { 22 | userId: session.user.id, 23 | access: "public", 24 | }, 25 | include: { 26 | user: true, 27 | media: true, 28 | }, 29 | orderBy: { createdAt: "desc" }, 30 | take: limit, 31 | skip: cursor ? 1 : 0, 32 | cursor: cursor ? { id: cursor } : undefined, 33 | }); 34 | 35 | const nextCursor = posts.length > 0 ? posts[posts.length - 1].id : null; 36 | 37 | return NextResponse.json( 38 | { 39 | data: posts, 40 | nextCursor: nextCursor, 41 | message: "posts fetched successfully", 42 | }, 43 | { status: 200 }, 44 | ); 45 | } catch (error) { 46 | console.error("Error fetching posts:", error); 47 | return NextResponse.json({ error: "error" }, { status: 500 }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/app/api/whoami/private/route.ts: -------------------------------------------------------------------------------- 1 | import getUserSession from "@/functions/get-user"; 2 | import { prisma } from "@/lib/prisma"; 3 | import { NextRequest, NextResponse } from "next/server"; 4 | 5 | export async function GET(request: NextRequest) { 6 | try { 7 | const session = await getUserSession(); 8 | 9 | if (!session) { 10 | return Response.json( 11 | { 12 | message: "unauthorized | not logged in", 13 | }, 14 | { status: 400 }, 15 | ); 16 | } 17 | const cursor = request.nextUrl.searchParams.get("cursor") || null; 18 | const limit = 5; 19 | 20 | const posts = await prisma.post.findMany({ 21 | where: { 22 | userId: session.user.id, 23 | access: "private", 24 | }, 25 | include: { 26 | user: true, 27 | media: true, 28 | }, 29 | orderBy: { createdAt: "desc" }, 30 | take: limit, 31 | skip: cursor ? 1 : 0, 32 | cursor: cursor ? { id: cursor } : undefined, 33 | }); 34 | 35 | const nextCursor = posts.length > 0 ? posts[posts.length - 1].id : null; 36 | 37 | return NextResponse.json( 38 | { 39 | data: posts, 40 | nextCursor: nextCursor, 41 | message: "posts fetched successfully", 42 | }, 43 | { status: 200 }, 44 | ); 45 | } catch (error) { 46 | console.error("Error fetching posts:", error); 47 | return NextResponse.json({ error: "error" }, { status: 500 }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/app/api/whoami/route.ts: -------------------------------------------------------------------------------- 1 | import getUserSession from "@/functions/get-user"; 2 | import { prisma } from "@/lib/prisma"; 3 | import { NextRequest, NextResponse } from "next/server"; 4 | 5 | export const GET = async (req: NextRequest) => { 6 | try { 7 | const session = await getUserSession(); 8 | 9 | if (!session) { 10 | return Response.json( 11 | { 12 | message: "unauthorized | not logged in", 13 | }, 14 | { status: 400 }, 15 | ); 16 | } 17 | 18 | const user = await prisma.user.findUnique({ 19 | where: { 20 | id: session.user.id, 21 | }, 22 | 23 | include: { 24 | posts: true, 25 | country: true, 26 | _count: { 27 | select: { 28 | following: true, 29 | followers: true, 30 | }, 31 | }, 32 | }, 33 | }); 34 | 35 | return NextResponse.json({ 36 | data: user, 37 | }); 38 | } catch (error) { 39 | console.error("Error fetching posts:", error); 40 | return NextResponse.json({ error: "error" }, { status: 500 }); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/src/app/favicon.ico -------------------------------------------------------------------------------- /src/app/onboarding/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect } from "react"; 4 | import { useRouter } from "next/navigation"; 5 | import { authClient } from "@/lib/auth-client"; 6 | import { OnboardingForm } from "@/components/onboarding-form"; 7 | 8 | export default function OnboardingPage() { 9 | const router = useRouter(); 10 | const session = authClient.useSession(); 11 | 12 | return ( 13 |
14 |
15 |
16 |

17 | Welcome to{" "} 18 | 19 | Nerdspace 20 | 21 |

22 |

23 | Let's set up your profile to help you connect with like-minded 24 | people 25 |

26 |
27 | 28 |
29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/app/post/[id]/layout.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from "react"; 2 | 3 | const PostExploreLayout = ({ children }: { children: ReactNode }) => { 4 | return
{children}
; 5 | }; 6 | 7 | export default PostExploreLayout; 8 | -------------------------------------------------------------------------------- /src/app/projects/recommendations/layout.tsx: -------------------------------------------------------------------------------- 1 | import Navbar from "@/components/navbar"; 2 | import React from "react"; 3 | 4 | const AppLayout = ({ children }: { children: React.ReactNode }) => { 5 | return ( 6 |
7 | 8 |
{children}
9 |
10 | ); 11 | }; 12 | 13 | export default AppLayout; 14 | -------------------------------------------------------------------------------- /src/app/settings/layout.tsx: -------------------------------------------------------------------------------- 1 | import Navbar from "@/components/navbar"; 2 | import React from "react"; 3 | 4 | const ProfileLayout = ({ children }: { children: React.ReactNode }) => { 5 | return ( 6 |
7 | 8 |
{children}
9 |
10 | ); 11 | }; 12 | 13 | export default ProfileLayout; 14 | -------------------------------------------------------------------------------- /src/app/settings/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import SettingsScreen from "@/components/app-sidebar"; 4 | import { SidebarProvider } from "@/components/ui/sidebar"; 5 | import { useSearchParams } from "next/navigation"; 6 | import { Suspense } from "react"; 7 | 8 | const ProfileScreenContent = () => { 9 | const searchParams = useSearchParams(); 10 | const tab = searchParams?.get("tab") || "profile"; 11 | 12 | return ( 13 | 14 |
15 | 16 |
17 |
18 | ); 19 | }; 20 | 21 | const ProfileScreen = () => { 22 | return ( 23 | Loading...
}> 24 | 25 | 26 | ); 27 | }; 28 | 29 | export default ProfileScreen; 30 | -------------------------------------------------------------------------------- /src/components/UserListSkeleton.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const UserListSkeleton = () => { 4 | // Create an array of 6 items to render skeleton cards 5 | const skeletonItems = Array.from({ length: 6 }, (_, i) => i); 6 | 7 | return ( 8 |
9 | {skeletonItems.map((item) => ( 10 |
15 |
16 |
17 |
18 |
19 | 20 |
21 |
22 | 23 |
24 |
25 |
26 |
27 | 28 |
29 |
30 |
31 | ))} 32 |
33 | ); 34 | }; 35 | 36 | export default UserListSkeleton; 37 | -------------------------------------------------------------------------------- /src/components/custom/image-upload.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import axios from "axios"; 3 | import { Input } from "@/components/ui/input"; 4 | import Image from "next/image"; 5 | 6 | interface ImageUploadProps { 7 | onUpload: (imageUrl: string) => void; 8 | } 9 | 10 | export default function ImageUploader({ onUpload }: ImageUploadProps) { 11 | const cloudinaryUploadUrl = process.env.NEXT_PUBLIC_CLOUDINARY_UPLOAD_URL!; 12 | const cloudinaryUploadPreset = process.env.NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET!; 13 | 14 | const [previewUrl, setPreviewUrl] = useState(null); 15 | 16 | const handleFileChange = async (e: React.ChangeEvent) => { 17 | if (e.target.files && e.target.files[0]) { 18 | const file = e.target.files[0]; 19 | 20 | // Generate a local preview 21 | const reader = new FileReader(); 22 | reader.onload = () => { 23 | setPreviewUrl(reader.result as string); 24 | }; 25 | reader.readAsDataURL(file); 26 | 27 | // Upload the image 28 | const formData = new FormData(); 29 | formData.append("file", file); 30 | formData.append("upload_preset", cloudinaryUploadPreset); 31 | 32 | try { 33 | const response = await axios.post(cloudinaryUploadUrl, formData); 34 | onUpload(response.data.secure_url); 35 | } catch (error) { 36 | console.error("Image upload failed:", error); 37 | } 38 | } 39 | }; 40 | 41 | return ( 42 |
43 | 44 | {previewUrl && ( 45 |
46 | Image Preview 47 |
48 | )} 49 |
50 | ); 51 | } -------------------------------------------------------------------------------- /src/components/follow-button.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Button } from "@/components/ui/button"; 4 | import { followService } from "@/functions/follow"; 5 | import { useState } from "react"; 6 | import { toast } from "sonner"; 7 | 8 | interface FollowButtonProps { 9 | userId: string; 10 | isFollowing: boolean; 11 | onFollowChange?: (isFollowing: boolean) => void; 12 | } 13 | 14 | export function FollowButton({ userId, isFollowing, onFollowChange }: FollowButtonProps) { 15 | const [isLoading, setIsLoading] = useState(false); 16 | 17 | const handleFollow = async () => { 18 | try { 19 | setIsLoading(true); 20 | const action = isFollowing ? "unfollow" : "follow"; 21 | await followService.followUser(userId, action); 22 | onFollowChange?.(!isFollowing); 23 | toast.success(`Successfully ${action}ed user`); 24 | } catch (error) { 25 | if (error instanceof Error) { 26 | if (error.message.includes("already following")) { 27 | // If we're already following, try to unfollow instead 28 | await followService.followUser(userId, "unfollow"); 29 | onFollowChange?.(false); 30 | toast.success("Successfully unfollowed user"); 31 | return; 32 | } 33 | toast.error(error.message); 34 | } else { 35 | toast.error("Failed to follow user"); 36 | } 37 | } finally { 38 | setIsLoading(false); 39 | } 40 | }; 41 | 42 | return ( 43 | 51 | ); 52 | } -------------------------------------------------------------------------------- /src/components/nav-secondary.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { type LucideIcon } from "lucide-react" 3 | 4 | import { 5 | SidebarGroup, 6 | SidebarGroupContent, 7 | SidebarMenu, 8 | SidebarMenuButton, 9 | SidebarMenuItem, 10 | } from "@/components/ui/sidebar" 11 | 12 | export function NavSecondary({ 13 | items, 14 | ...props 15 | }: { 16 | items: { 17 | title: string 18 | url: string 19 | icon: LucideIcon 20 | }[] 21 | } & React.ComponentPropsWithoutRef) { 22 | return ( 23 | 24 | 25 | 26 | {items.map((item) => ( 27 | 28 | 29 | 30 | 31 | {item.title} 32 | 33 | 34 | 35 | ))} 36 | 37 | 38 | 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /src/components/navbar/right-navbar.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect, useState } from "react"; 4 | import { authClient } from "@/lib/auth-client"; 5 | import { prisma } from "@/lib/prisma"; 6 | import FollowList from "../user/followList"; 7 | import RecomendedProjects from "../user/recommend-project"; 8 | 9 | const RightNavbar = () => { 10 | const [users, setUsers] = useState([]); 11 | const [loading, setLoading] = useState(true); 12 | 13 | useEffect(() => { 14 | const fetchUsers = async () => { 15 | try { 16 | const user = await authClient.getSession(); 17 | const response = await fetch(`/api/users?exclude=${user?.data?.user.id}&limit=5`); 18 | const data = await response.json(); 19 | setUsers(data); 20 | } catch (error) { 21 | console.error("Error fetching users:", error); 22 | } finally { 23 | setLoading(false); 24 | } 25 | }; 26 | 27 | fetchUsers(); 28 | }, []); 29 | 30 | return ( 31 |
32 | 33 |
34 | 35 |
36 |
37 | ); 38 | }; 39 | 40 | export default RightNavbar; 41 | -------------------------------------------------------------------------------- /src/components/post/comment/DeleteCommentModal.tsx: -------------------------------------------------------------------------------- 1 | import { useMutation, useQueryClient } from "@tanstack/react-query"; 2 | import axios from "axios"; 3 | import { Button } from "@/components/ui/button"; 4 | import toast from "react-hot-toast"; 5 | import { 6 | Dialog, 7 | DialogContent, 8 | DialogHeader, 9 | DialogTitle, 10 | DialogDescription, 11 | DialogFooter, 12 | } from "@/components/ui/dialog"; 13 | 14 | interface DeleteCommentModalProps { 15 | commentId: string; 16 | isOpen: boolean; 17 | onClose: () => void; 18 | } 19 | 20 | // TODO: Add a confirmation modal 21 | const DeleteCommentModal: React.FC = ({ 22 | commentId, 23 | isOpen, 24 | onClose, 25 | 26 | }) => { 27 | const queryClient = useQueryClient(); 28 | 29 | const mutation = useMutation({ 30 | mutationFn: async () => { 31 | await axios.delete(`/api/post/comment?commentId=${commentId}`); 32 | }, 33 | onSuccess: () => { 34 | queryClient.invalidateQueries({ queryKey: ["comment"] }); 35 | toast.success("Comment deleted successfully"); 36 | onClose(); 37 | }, 38 | onError: () => { 39 | toast.error("Error occurred while deleting comment"); 40 | }, 41 | }); 42 | 43 | const handleDelete = () => { 44 | mutation.mutate(); 45 | }; 46 | 47 | return ( 48 | 49 | 50 | 51 | Delete Comment 52 | 53 | Are you sure you want to delete this comment? 54 | 55 | 56 | 57 | 60 | 67 | 68 | 69 | 70 | ); 71 | }; 72 | 73 | export default DeleteCommentModal; 74 | -------------------------------------------------------------------------------- /src/components/post/comment/comment.tsx: -------------------------------------------------------------------------------- 1 | import CommentSkeleton from "@/components/skeleton/comment.skelton"; 2 | import { Button } from "@/components/ui/button"; 3 | import { SendIcon } from "lucide-react"; 4 | import React, { JSX } from "react"; 5 | import PostCommentInterface from "@/interface/auth/comment.interface"; 6 | 7 | interface CommentComponentProps { 8 | commentContent: string; 9 | setCommentContent: React.Dispatch>; 10 | handleCommentSubmit: () => void; 11 | commentLoading: boolean; 12 | renderComments: (comments: PostCommentInterface[], parentId: string | null, level?: number) => JSX.Element[]; 13 | comment: PostCommentInterface[]; 14 | } 15 | 16 | const CommentComponent: React.FC = ({ 17 | commentContent, 18 | setCommentContent, 19 | handleCommentSubmit, 20 | commentLoading, 21 | renderComments, 22 | comment, 23 | }) => { 24 | return ( 25 |
26 |
27 |
28 | setCommentContent(e.target.value)} 33 | /> 34 | 40 |
41 | {commentLoading && } 42 |
{renderComments(comment, null)}
43 |
44 | ); 45 | }; 46 | 47 | export default CommentComponent; 48 | -------------------------------------------------------------------------------- /src/components/quality-notice.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Bell, Info, ImagePlus } from "lucide-react"; 4 | import { useState } from "react"; 5 | import { Button } from "./ui/button"; 6 | 7 | const QualityNotice = () => { 8 | const [showQualityTip, setShowQualityTip] = useState(false); 9 | 10 | return ( 11 |
setShowQualityTip(true)} 14 | onMouseLeave={() => setShowQualityTip(false)} 15 | > 16 | 26 | 27 | {showQualityTip && ( 28 |
29 |
30 | 31 |
32 |

Quality Guidelines

33 |

34 | Please ensure your images are high-quality and relevant to maintain our community standards 📸✨ 35 |

36 |
37 |
38 |
39 | )} 40 |
41 | ); 42 | }; 43 | 44 | export default QualityNotice; -------------------------------------------------------------------------------- /src/components/search-form.tsx: -------------------------------------------------------------------------------- 1 | import { Search } from "lucide-react" 2 | 3 | import { Label } from "@/components/ui/label" 4 | import { SidebarInput } from "@/components/ui/sidebar" 5 | 6 | export function SearchForm({ ...props }: React.ComponentProps<"form">) { 7 | return ( 8 |
9 |
10 | 13 | 18 | 19 |
20 |
21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /src/components/settings/notification-setting.tsx: -------------------------------------------------------------------------------- 1 | const NotificationSetting = () => { 2 | return ( 3 |
4 | Notification Setting 5 |
6 | ) 7 | } 8 | 9 | export default NotificationSetting; 10 | -------------------------------------------------------------------------------- /src/components/settings/tabs/CollectionsTab.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | export default function CollectionsTab() { 4 | return ( 5 |
6 | {Array.from({ length: 2 }).map((_, i) => ( 7 |
8 |
9 | {Array.from({ length: 4 }).map((_, j) => ( 10 |
11 | {`Collection 18 |
19 | ))} 20 |
21 |
22 |

Collection {i + 1}

23 |

{4} items

24 |
25 |
26 | ))} 27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/components/settings/tabs/PrivateTab.tsx: -------------------------------------------------------------------------------- 1 | import RenderMyPrivatePost from "../my-private-post"; 2 | 3 | export default function PrivateTab() { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /src/components/sidebar/nav-main.tsx: -------------------------------------------------------------------------------- 1 | import type React from "react" 2 | import { ChevronRight } from "lucide-react" 3 | import { SidebarMenu, SidebarMenuButton } from "@/components/ui/sidebar" 4 | import { cn } from "@/lib/utils" 5 | 6 | interface NavMainProps { 7 | items: { 8 | title: string 9 | url: string 10 | icon: React.ReactNode 11 | isActive?: boolean 12 | items?: { 13 | title: string 14 | url: string 15 | }[] 16 | }[] 17 | } 18 | 19 | export function NavMain({ items }: NavMainProps) { 20 | return ( 21 |
22 |
23 |

Settings

24 |
25 | 26 | {items.map((item) => ( 27 |
28 | 29 |
30 | {item.icon} 31 | {item.title} 32 |
33 | {item.items && item.items.length > 0 && } 34 |
35 |
36 | ))} 37 |
38 |
39 | ) 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/components/sidebar/nav-projects.tsx: -------------------------------------------------------------------------------- 1 | import type React from "react" 2 | import { Plus } from "lucide-react" 3 | import { SidebarMenu, SidebarMenuButton } from "@/components/ui/sidebar" 4 | 5 | interface NavProjectsProps { 6 | projects: { 7 | name: string 8 | url: string 9 | icon: React.ReactNode 10 | }[] 11 | } 12 | 13 | export function NavProjects({ projects }: NavProjectsProps) { 14 | return ( 15 |
16 |
17 |

Communities

18 | 21 |
22 | 23 | {projects.map((project) => ( 24 |
25 | 26 |
27 | {project.icon} 28 | {project.name} 29 |
30 |
31 |
32 | ))} 33 |
34 |
35 | ) 36 | } 37 | 38 | -------------------------------------------------------------------------------- /src/components/sidebar/nav-secondary.tsx: -------------------------------------------------------------------------------- 1 | import type React from "react" 2 | import { SidebarMenu, SidebarMenuButton } from "@/components/ui/sidebar" 3 | 4 | interface NavSecondaryProps extends React.HTMLAttributes { 5 | items: { 6 | title: string 7 | url: string 8 | icon: React.ReactNode 9 | }[] 10 | } 11 | 12 | export function NavSecondary({ items, className, ...props }: NavSecondaryProps) { 13 | return ( 14 |
15 | 16 | {items.map((item) => ( 17 |
18 | 19 |
20 | {item.icon} 21 | {item.title} 22 |
23 |
24 |
25 | ))} 26 |
27 |
28 | ) 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/components/sidebar/nav-user.tsx: -------------------------------------------------------------------------------- 1 | import { ChevronRight } from "lucide-react" 2 | import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" 3 | 4 | interface NavUserProps { 5 | user: { 6 | name: string 7 | email: string 8 | avatar: string 9 | } 10 | } 11 | 12 | export function NavUser({ user }: NavUserProps) { 13 | return ( 14 | 25 | ) 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/components/site-header.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { SidebarIcon } from "lucide-react" 4 | 5 | import { SearchForm } from "@/components/search-form" 6 | import { 7 | Breadcrumb, 8 | BreadcrumbItem, 9 | BreadcrumbLink, 10 | BreadcrumbList, 11 | BreadcrumbPage, 12 | BreadcrumbSeparator, 13 | } from "@/components/ui/breadcrumb" 14 | import { Button } from "@/components/ui/button" 15 | import { Separator } from "@/components/ui/separator" 16 | import { useSidebar } from "@/components/ui/sidebar" 17 | 18 | export function SiteHeader() { 19 | const { toggleSidebar } = useSidebar() 20 | 21 | return ( 22 |
23 |
24 | 32 | 33 | 34 | 35 | 36 | 37 | Building Your Application 38 | 39 | 40 | 41 | 42 | Data Fetching 43 | 44 | 45 | 46 | 47 |
48 |
49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /src/components/skeleton/comment.skelton.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Skeleton } from "../ui/skeleton"; 3 | 4 | const CommentSkeleton = () => { 5 | return ( 6 |
7 | 8 |
9 | 10 |
11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | 20 | 21 | 22 |
23 | 24 |
25 |
26 | 27 |
28 |
29 | 30 |
31 |
32 | {/*
*/} 33 |
34 | 35 | 36 |
37 | ); 38 | }; 39 | 40 | export default CommentSkeleton; 41 | -------------------------------------------------------------------------------- /src/components/skeleton/morepostFetch.skeleton.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Skeleton } from "../ui/skeleton"; 3 | import { Card } from "../ui/card"; 4 | 5 | const MorePostsFetchSkeleton = () => { 6 | return ( 7 |
8 | 9 |
10 | 11 |
12 | 13 | 14 |
15 |
16 | 17 |
18 | 19 | 20 | 21 |
22 |
23 |
24 | ); 25 | }; 26 | 27 | export default MorePostsFetchSkeleton; 28 | -------------------------------------------------------------------------------- /src/components/soon/soon.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ArrowRight } from "lucide-react"; 4 | import { useRouter } from "next/navigation"; 5 | import { Button } from "../ui/button"; 6 | const SoonComponent = () => { 7 | const router = useRouter(); 8 | 9 | return ( 10 |
11 |

12 | Under Development 13 |

14 |

15 | We're working hard to bring you this feature. Thank you for your 16 | patience while we perfect it. 17 |

18 | 26 |
27 | ); 28 | }; 29 | 30 | export default SoonComponent; 31 | -------------------------------------------------------------------------------- /src/components/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import { ThemeProvider as NextThemesProvider } from "next-themes" 5 | 6 | export function ThemeProvider({ 7 | children, 8 | ...props 9 | }: React.ComponentProps) { 10 | return {children} 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/toaster.tsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeabnoah/Nerdspace_codebase1/011d459664f499b2a0fd7d898f93c070d62e34b7/src/components/toaster.tsx -------------------------------------------------------------------------------- /src/components/ui/alert.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { cva, type VariantProps } from "class-variance-authority" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const alertVariants = cva( 7 | "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7", 8 | { 9 | variants: { 10 | variant: { 11 | default: "bg-background text-foreground", 12 | destructive: 13 | "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", 14 | }, 15 | }, 16 | defaultVariants: { 17 | variant: "default", 18 | }, 19 | } 20 | ) 21 | 22 | const Alert = React.forwardRef< 23 | HTMLDivElement, 24 | React.HTMLAttributes & VariantProps 25 | >(({ className, variant, ...props }, ref) => ( 26 |
32 | )) 33 | Alert.displayName = "Alert" 34 | 35 | const AlertTitle = React.forwardRef< 36 | HTMLParagraphElement, 37 | React.HTMLAttributes 38 | >(({ className, ...props }, ref) => ( 39 |
44 | )) 45 | AlertTitle.displayName = "AlertTitle" 46 | 47 | const AlertDescription = React.forwardRef< 48 | HTMLParagraphElement, 49 | React.HTMLAttributes 50 | >(({ className, ...props }, ref) => ( 51 |
56 | )) 57 | AlertDescription.displayName = "AlertDescription" 58 | 59 | export { Alert, AlertTitle, AlertDescription } 60 | -------------------------------------------------------------------------------- /src/components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" 4 | 5 | const AspectRatio = AspectRatioPrimitive.Root 6 | 7 | export { AspectRatio } 8 | -------------------------------------------------------------------------------- /src/components/ui/avatar.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as AvatarPrimitive from "@radix-ui/react-avatar" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Avatar = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, ...props }, ref) => ( 12 | 20 | )) 21 | Avatar.displayName = AvatarPrimitive.Root.displayName 22 | 23 | const AvatarImage = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, ...props }, ref) => ( 27 | 32 | )) 33 | AvatarImage.displayName = AvatarPrimitive.Image.displayName 34 | 35 | const AvatarFallback = React.forwardRef< 36 | React.ElementRef, 37 | React.ComponentPropsWithoutRef 38 | >(({ className, ...props }, ref) => ( 39 | 47 | )) 48 | AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName 49 | 50 | export { Avatar, AvatarImage, AvatarFallback } 51 | -------------------------------------------------------------------------------- /src/components/ui/badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { cva, type VariantProps } from "class-variance-authority" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const badgeVariants = cva( 7 | "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", 8 | { 9 | variants: { 10 | variant: { 11 | default: 12 | "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", 13 | secondary: 14 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", 15 | destructive: 16 | "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", 17 | outline: "text-foreground", 18 | }, 19 | }, 20 | defaultVariants: { 21 | variant: "default", 22 | }, 23 | } 24 | ) 25 | 26 | export interface BadgeProps 27 | extends React.HTMLAttributes, 28 | VariantProps {} 29 | 30 | function Badge({ className, variant, ...props }: BadgeProps) { 31 | return ( 32 |
33 | ) 34 | } 35 | 36 | export { Badge, badgeVariants } 37 | -------------------------------------------------------------------------------- /src/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import { Slot } from "@radix-ui/react-slot" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | import { cn } from "@/lib/utils" 7 | 8 | const buttonVariants = cva( 9 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", 10 | { 11 | variants: { 12 | variant: { 13 | default: 14 | "bg-primary text-primary-foreground shadow hover:bg-primary/90", 15 | destructive: 16 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", 17 | outline: 18 | "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", 19 | secondary: 20 | "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", 21 | ghost: "hover:bg-accent hover:text-accent-foreground", 22 | link: "text-primary underline-offset-4 hover:underline", 23 | }, 24 | size: { 25 | default: "h-9 px-4 py-2", 26 | sm: "h-8 rounded-md px-3 text-xs", 27 | lg: "h-10 rounded-md px-8", 28 | icon: "h-9 w-9", 29 | }, 30 | }, 31 | defaultVariants: { 32 | variant: "default", 33 | size: "default", 34 | }, 35 | } 36 | ) 37 | 38 | export interface ButtonProps 39 | extends React.ButtonHTMLAttributes, 40 | VariantProps { 41 | asChild?: boolean 42 | } 43 | 44 | const Button = React.forwardRef( 45 | ({ className, variant, size, asChild = false, ...props }, ref) => { 46 | const Comp = asChild ? Slot : "button" 47 | return ( 48 | 53 | ) 54 | } 55 | ) 56 | Button.displayName = "Button" 57 | 58 | export { Button, buttonVariants } 59 | -------------------------------------------------------------------------------- /src/components/ui/checkbox.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as CheckboxPrimitive from "@radix-ui/react-checkbox" 5 | import { Check } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const Checkbox = React.forwardRef< 10 | React.ElementRef, 11 | React.ComponentPropsWithoutRef 12 | >(({ className, ...props }, ref) => ( 13 | 21 | 24 | 25 | 26 | 27 | )) 28 | Checkbox.displayName = CheckboxPrimitive.Root.displayName 29 | 30 | export { Checkbox } 31 | -------------------------------------------------------------------------------- /src/components/ui/collapsible.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" 4 | 5 | const Collapsible = CollapsiblePrimitive.Root 6 | 7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger 8 | 9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent 10 | 11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent } 12 | -------------------------------------------------------------------------------- /src/components/ui/hover-card.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as HoverCardPrimitive from "@radix-ui/react-hover-card" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const HoverCard = HoverCardPrimitive.Root 9 | 10 | const HoverCardTrigger = HoverCardPrimitive.Trigger 11 | 12 | const HoverCardContent = React.forwardRef< 13 | React.ElementRef, 14 | React.ComponentPropsWithoutRef 15 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( 16 | 26 | )) 27 | HoverCardContent.displayName = HoverCardPrimitive.Content.displayName 28 | 29 | export { HoverCard, HoverCardTrigger, HoverCardContent } 30 | -------------------------------------------------------------------------------- /src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Input = React.forwardRef>( 6 | ({ className, type, ...props }, ref) => { 7 | return ( 8 | 17 | ) 18 | } 19 | ) 20 | Input.displayName = "Input" 21 | 22 | export { Input } 23 | -------------------------------------------------------------------------------- /src/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 | -------------------------------------------------------------------------------- /src/components/ui/loading-spinner.tsx: -------------------------------------------------------------------------------- 1 | export function LoadingSpinner() { 2 | return ( 3 |
4 |
5 |
6 | ) 7 | } 8 | 9 | -------------------------------------------------------------------------------- /src/components/ui/popover.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as PopoverPrimitive from "@radix-ui/react-popover" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Popover = PopoverPrimitive.Root 9 | 10 | const PopoverTrigger = PopoverPrimitive.Trigger 11 | 12 | const PopoverAnchor = PopoverPrimitive.Anchor 13 | 14 | const PopoverContent = React.forwardRef< 15 | React.ElementRef, 16 | React.ComponentPropsWithoutRef 17 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( 18 | 19 | 29 | 30 | )) 31 | PopoverContent.displayName = PopoverPrimitive.Content.displayName 32 | 33 | export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } 34 | -------------------------------------------------------------------------------- /src/components/ui/progress.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface ProgressProps { 4 | value: number; 5 | className?: string; 6 | label?: string; 7 | } 8 | 9 | export const Progress: React.FC = ({ 10 | value, 11 | className, 12 | label, 13 | }) => { 14 | return ( 15 |
16 |
20 | {label && ( 21 | 22 | {label} 23 | 24 | )} 25 |
26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /src/components/ui/radio-group.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" 5 | import { Circle } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const RadioGroup = React.forwardRef< 10 | React.ElementRef, 11 | React.ComponentPropsWithoutRef 12 | >(({ className, ...props }, ref) => { 13 | return ( 14 | 19 | ) 20 | }) 21 | RadioGroup.displayName = RadioGroupPrimitive.Root.displayName 22 | 23 | const RadioGroupItem = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, ...props }, ref) => { 27 | return ( 28 | 36 | 37 | 38 | 39 | 40 | ) 41 | }) 42 | RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName 43 | 44 | export { RadioGroup, RadioGroupItem } 45 | -------------------------------------------------------------------------------- /src/components/ui/resizable.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { GripVertical } from "lucide-react" 4 | import * as ResizablePrimitive from "react-resizable-panels" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const ResizablePanelGroup = ({ 9 | className, 10 | ...props 11 | }: React.ComponentProps) => ( 12 | 19 | ) 20 | 21 | const ResizablePanel = ResizablePrimitive.Panel 22 | 23 | const ResizableHandle = ({ 24 | withHandle, 25 | className, 26 | ...props 27 | }: React.ComponentProps & { 28 | withHandle?: boolean 29 | }) => ( 30 | div]:rotate-90", 33 | className 34 | )} 35 | {...props} 36 | > 37 | {withHandle && ( 38 |
39 | 40 |
41 | )} 42 |
43 | ) 44 | 45 | export { ResizablePanelGroup, ResizablePanel, ResizableHandle } 46 | -------------------------------------------------------------------------------- /src/components/ui/scroll-area.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const ScrollArea = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, children, ...props }, ref) => ( 12 | 17 | 18 | {children} 19 | 20 | 21 | 22 | 23 | )) 24 | ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName 25 | 26 | const ScrollBar = React.forwardRef< 27 | React.ElementRef, 28 | React.ComponentPropsWithoutRef 29 | >(({ className, orientation = "vertical", ...props }, ref) => ( 30 | 43 | 44 | 45 | )) 46 | ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName 47 | 48 | export { ScrollArea, ScrollBar } 49 | -------------------------------------------------------------------------------- /src/components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SeparatorPrimitive from "@radix-ui/react-separator" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Separator = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >( 12 | ( 13 | { className, orientation = "horizontal", decorative = true, ...props }, 14 | ref 15 | ) => ( 16 | 27 | ) 28 | ) 29 | Separator.displayName = SeparatorPrimitive.Root.displayName 30 | 31 | export { Separator } 32 | -------------------------------------------------------------------------------- /src/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ) 13 | } 14 | 15 | export { Skeleton } 16 | -------------------------------------------------------------------------------- /src/components/ui/slider.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SliderPrimitive from "@radix-ui/react-slider" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Slider = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, ...props }, ref) => ( 12 | 20 | 21 | 22 | 23 | 24 | 25 | )) 26 | Slider.displayName = SliderPrimitive.Root.displayName 27 | 28 | export { Slider } 29 | -------------------------------------------------------------------------------- /src/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 | -------------------------------------------------------------------------------- /src/components/ui/switch.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SwitchPrimitives from "@radix-ui/react-switch" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Switch = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, ...props }, ref) => ( 12 | 20 | 25 | 26 | )) 27 | Switch.displayName = SwitchPrimitives.Root.displayName 28 | 29 | export { Switch } 30 | -------------------------------------------------------------------------------- /src/components/ui/tabs.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as TabsPrimitive from "@radix-ui/react-tabs" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Tabs = TabsPrimitive.Root 9 | 10 | const TabsList = React.forwardRef< 11 | React.ElementRef, 12 | React.ComponentPropsWithoutRef 13 | >(({ className, ...props }, ref) => ( 14 | 22 | )) 23 | TabsList.displayName = TabsPrimitive.List.displayName 24 | 25 | const TabsTrigger = React.forwardRef< 26 | React.ElementRef, 27 | React.ComponentPropsWithoutRef 28 | >(({ className, ...props }, ref) => ( 29 | 37 | )) 38 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName 39 | 40 | const TabsContent = React.forwardRef< 41 | React.ElementRef, 42 | React.ComponentPropsWithoutRef 43 | >(({ className, ...props }, ref) => ( 44 | 52 | )) 53 | TabsContent.displayName = TabsPrimitive.Content.displayName 54 | 55 | export { Tabs, TabsList, TabsTrigger, TabsContent } 56 | -------------------------------------------------------------------------------- /src/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 |