├── .dockerignore
├── .env.example
├── .eslintrc.json
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── components.json
├── docker-compose.yml
├── next.config.mjs
├── package.json
├── pfp_seed.js
├── pnpm-lock.yaml
├── postcss.config.js
├── prisma
└── schema.prisma
├── public
├── images
│ ├── badge-launch.svg
│ ├── badge-month.svg
│ ├── badge-week.svg
│ ├── repository-screenshot.png
│ ├── roadmapai-screenshot.png
│ └── wikipedia.png
├── opengraph-image.png
└── roadmapai.svg
├── src
├── actions
│ ├── roadmaps.ts
│ └── users.ts
├── app
│ ├── (auth)
│ │ ├── layout.tsx
│ │ ├── sign-in
│ │ │ └── [[...sign-in]]
│ │ │ │ └── page.tsx
│ │ └── sign-up
│ │ │ └── [[...sign-up]]
│ │ │ └── page.tsx
│ ├── (private)
│ │ ├── dashboard
│ │ │ ├── layout.tsx
│ │ │ ├── loading.tsx
│ │ │ └── page.tsx
│ │ └── roadmap
│ │ │ ├── [id]
│ │ │ └── page.tsx
│ │ │ └── page.tsx
│ ├── (public)
│ │ └── explore
│ │ │ └── page.tsx
│ ├── api
│ │ ├── health
│ │ │ └── route.ts
│ │ ├── og
│ │ │ └── [title]
│ │ │ │ ├── base-og.png
│ │ │ │ └── route.tsx
│ │ ├── v1
│ │ │ ├── cohere
│ │ │ │ ├── details
│ │ │ │ │ └── route.ts
│ │ │ │ └── roadmap
│ │ │ │ │ └── route.ts
│ │ │ ├── gemini
│ │ │ │ ├── details
│ │ │ │ │ └── route.ts
│ │ │ │ └── roadmap
│ │ │ │ │ └── route.ts
│ │ │ ├── groq
│ │ │ │ ├── details
│ │ │ │ │ └── route.ts
│ │ │ │ └── roadmap
│ │ │ │ │ └── route.ts
│ │ │ ├── openai
│ │ │ │ ├── details
│ │ │ │ │ └── route.ts
│ │ │ │ └── roadmap
│ │ │ │ │ └── route.ts
│ │ │ ├── orilley
│ │ │ │ └── route.ts
│ │ │ └── roadmaps
│ │ │ │ └── route.ts
│ │ └── webhook
│ │ │ └── route.ts
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ ├── opengraph-image.png
│ └── page.tsx
├── components
│ ├── ApiKeyDialog.tsx
│ ├── alerts
│ │ ├── EmptyAlert.tsx
│ │ ├── ErrorAlert.tsx
│ │ └── SearchAlert.tsx
│ ├── app
│ │ ├── appbar.tsx
│ │ ├── icons.tsx
│ │ ├── mobile-drawer.tsx
│ │ ├── nav-items.tsx
│ │ └── providers.tsx
│ ├── flow-components
│ │ ├── Flow.tsx
│ │ ├── Instructions.tsx
│ │ ├── Search.tsx
│ │ ├── custom-node.tsx
│ │ ├── data.js
│ │ ├── drawer.tsx
│ │ ├── expand-collapse.tsx
│ │ ├── explore-pagination.tsx
│ │ ├── generate-button.tsx
│ │ ├── generator-controls.tsx
│ │ ├── model-select.tsx
│ │ ├── react-flow-pro.tsx
│ │ └── roadmap-card.tsx
│ ├── landing
│ │ ├── roadmap-confetti.tsx
│ │ ├── roadmap-features.tsx
│ │ ├── roadmap-footer.tsx
│ │ ├── roadmap-hero.tsx
│ │ ├── roadmap-icon-cloud.tsx
│ │ ├── roadmap-pricing.tsx
│ │ ├── roadmap-stats.tsx
│ │ └── roadmap-team.tsx
│ ├── magicui
│ │ ├── dot-pattern.tsx
│ │ └── icon-cloud.tsx
│ ├── marketing
│ │ ├── banner.tsx
│ │ ├── cta.tsx
│ │ └── text-ticker.tsx
│ ├── roadmap
│ │ ├── code-viewer.tsx
│ │ ├── data
│ │ │ ├── models.ts
│ │ │ └── presets.ts
│ │ ├── maxlength-selector.tsx
│ │ ├── model-selector.tsx
│ │ ├── preset-save.tsx
│ │ ├── preset-selector.tsx
│ │ ├── preset-share.tsx
│ │ ├── roadmap.tsx
│ │ ├── temperature-selector.tsx
│ │ └── top-p-selector.tsx
│ ├── testimonials
│ │ └── roadmap-testimonial.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
│ │ ├── checkbox.tsx
│ │ ├── collapsible.tsx
│ │ ├── command.tsx
│ │ ├── context-menu.tsx
│ │ ├── dialog.tsx
│ │ ├── dot-pattern.tsx
│ │ ├── drawer.tsx
│ │ ├── dropdown-menu.tsx
│ │ ├── form.tsx
│ │ ├── hover-card.tsx
│ │ ├── input-otp.tsx
│ │ ├── input.tsx
│ │ ├── label.tsx
│ │ ├── marque-wrapper.tsx
│ │ ├── marquee.tsx
│ │ ├── menubar.tsx
│ │ ├── navigation-menu.tsx
│ │ ├── neobrutalism-button.tsx
│ │ ├── pagination.tsx
│ │ ├── popover.tsx
│ │ ├── progress.tsx
│ │ ├── radio-group.tsx
│ │ ├── resizable.tsx
│ │ ├── scroll-area.tsx
│ │ ├── select.tsx
│ │ ├── separator.tsx
│ │ ├── sheet.tsx
│ │ ├── skeleton.tsx
│ │ ├── skew-scroll.tsx
│ │ ├── slider.tsx
│ │ ├── sonner.tsx
│ │ ├── switch.tsx
│ │ ├── table.tsx
│ │ ├── tabs.tsx
│ │ ├── textarea.tsx
│ │ ├── three-d-button.tsx
│ │ ├── toast.tsx
│ │ ├── toaster.tsx
│ │ ├── toggle-group.tsx
│ │ ├── toggle.tsx
│ │ ├── tooltip.tsx
│ │ └── use-toast.ts
├── fonts
│ ├── inter
│ │ ├── Inter-Black.ttf
│ │ ├── Inter-Bold.ttf
│ │ ├── Inter-ExtraBold.ttf
│ │ ├── Inter-ExtraLight.ttf
│ │ ├── Inter-Light.ttf
│ │ ├── Inter-Medium.ttf
│ │ ├── Inter-Regular.ttf
│ │ ├── Inter-SemiBold.ttf
│ │ ├── Inter-Thin.ttf
│ │ └── Inter-VariableFont.ttf
│ └── nunito
│ │ ├── Nunito-Black.ttf
│ │ ├── Nunito-BlackItalic.ttf
│ │ ├── Nunito-Bold.ttf
│ │ ├── Nunito-BoldItalic.ttf
│ │ ├── Nunito-ExtraBold.ttf
│ │ ├── Nunito-ExtraBoldItalic.ttf
│ │ ├── Nunito-ExtraLight.ttf
│ │ ├── Nunito-ExtraLightItalic.ttf
│ │ ├── Nunito-Italic.ttf
│ │ ├── Nunito-Light.ttf
│ │ ├── Nunito-LightItalic.ttf
│ │ ├── Nunito-Medium.ttf
│ │ ├── Nunito-MediumItalic.ttf
│ │ ├── Nunito-Regular.ttf
│ │ ├── Nunito-SemiBold.ttf
│ │ └── Nunito-SemiBoldItalic.ttf
├── hooks
│ ├── use-animated-nodes.ts
│ ├── use-has-mounted.ts
│ └── use-mutation-observer.ts
├── lib
│ ├── crisp.ts
│ ├── db.ts
│ ├── flow.ts
│ ├── knowledge-graph.ts
│ ├── queries.ts
│ ├── shared
│ │ ├── constants.ts
│ │ ├── temp-data.ts
│ │ ├── tree.js
│ │ ├── types
│ │ │ └── common.ts
│ │ └── urls.ts
│ ├── stores
│ │ ├── index.ts
│ │ ├── useFeatureFlag.ts
│ │ └── useUI.ts
│ ├── types.ts
│ ├── utils.ts
│ ├── validations
│ │ ├── og.ts
│ │ └── roadmap.ts
│ └── youtube.ts
└── middleware.ts
├── tailwind.config.ts
└── tsconfig.json
/.dockerignore:
--------------------------------------------------------------------------------
1 | Dockerfile
2 | .dockerignore
3 | node_modules
4 | npm-debug.log
5 | README.md
6 | .next
7 | .git
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | # NOTE: This file is an example of the .env file that should be created in the root of the project.
2 | # To run the project locally in development keep the development environment variables and comment out the production environment variables.
3 |
4 | # DEVELOPMENT ENVIRONMENT VARIABLES
5 |
6 | # Database
7 | DATABASE_URL=""
8 |
9 | # API Keys
10 | OPENAI_API_KEY=""
11 | GEMINI_API_KEY=""
12 | COHERE_API_KEY=""
13 | GROQ_API_KEY=""
14 |
15 | # Youtube
16 | YOUTUBE_API_KEY=""
17 |
18 | # Crisp Chat Keys
19 | NEXT_PUBLIC_CRISP_WEBSITE_ID=""
20 |
21 | # Clerk Development Keys
22 | CLERK_SECRET_KEY=""
23 | WEBHOOK_SECRET=""
24 | NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=""
25 | NEXT_PUBLIC_CLERK_SIGN_IN_URL="/sign-in"
26 | NEXT_PUBLIC_CLERK_SIGN_UP_URL="/sign-up"
27 | NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL="/dashboard"
28 | NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL="/"
29 |
30 | # ========================================
31 |
32 | # PRODUCTION ENVIRONMENT VARIABLES
33 |
34 | # Database
35 | DATABASE_URL=""
36 |
37 | # API Keys
38 | OPENAI_API_KEY=""
39 | GEMINI_API_KEY=""
40 | COHERE_API_KEY=""
41 | GROQ_API_KEY=""
42 |
43 | # Youtube
44 | YOUTUBE_API_KEY=""
45 |
46 | # Crisp Chat Keys
47 | NEXT_PUBLIC_CRISP_WEBSITE_ID=""
48 |
49 | # Clerk Production Keys
50 | CLERK_SECRET_KEY=""
51 | WEBHOOK_SECRET=""
52 | NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=""
53 | NEXT_PUBLIC_CLERK_SIGN_IN_URL="/sign-in"
54 | NEXT_PUBLIC_CLERK_SIGN_UP_URL="/sign-up"
55 | NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL="/dashboard"
56 | NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL="/"
57 |
58 | # Portainer stack gitops webhook
59 | WEBHOOK_URL="your-webook-url"
60 |
61 | # Docker image URL
62 | DOCKER_IMAGE_URL="your-docker-image-url"
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env
30 | .env*.local
31 | .env*.development
32 | .env*.production
33 |
34 | # vercel
35 | .vercel
36 |
37 | # typescript
38 | *.tsbuildinfo
39 | next-env.d.ts
40 |
41 | # prisma migrations
42 | prisma/migrations
43 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:18-alpine AS base
2 |
3 | # Install dependencies only when needed
4 | FROM base AS deps
5 | # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
6 | RUN apk add --no-cache libc6-compat
7 | WORKDIR /app
8 |
9 | # Install dependencies based on the preferred package manager
10 | COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
11 | RUN \
12 | if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
13 | elif [ -f package-lock.json ]; then npm ci; \
14 | elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
15 | else echo "Lockfile not found." && exit 1; \
16 | fi
17 |
18 |
19 | # Rebuild the source code only when needed
20 | FROM base AS builder
21 | WORKDIR /app
22 | COPY --from=deps /app/node_modules ./node_modules
23 | COPY . .
24 |
25 | # Next.js collects completely anonymous telemetry data about general usage.
26 | # Learn more here: https://nextjs.org/telemetry
27 | # Uncomment the following line in case you want to disable telemetry during the build.
28 | # ENV NEXT_TELEMETRY_DISABLED 1
29 |
30 | RUN \
31 | if [ -f yarn.lock ]; then yarn run build; \
32 | elif [ -f package-lock.json ]; then npm run build; \
33 | elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
34 | else echo "Lockfile not found." && exit 1; \
35 | fi
36 |
37 | # Production image, copy all the files and run next
38 | FROM base AS runner
39 | WORKDIR /app
40 |
41 | ENV NODE_ENV production
42 | # Uncomment the following line in case you want to disable telemetry during runtime.
43 | # ENV NEXT_TELEMETRY_DISABLED 1
44 |
45 | RUN addgroup --system --gid 1001 nodejs
46 | RUN adduser --system --uid 1001 nextjs
47 |
48 | COPY --from=builder /app/public ./public
49 |
50 | # Set the correct permission for prerender cache
51 | RUN mkdir .next
52 | RUN chown nextjs:nodejs .next
53 |
54 | # Automatically leverage output traces to reduce image size
55 | # https://nextjs.org/docs/advanced-features/output-file-tracing
56 | COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
57 | COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
58 |
59 | USER nextjs
60 |
61 | EXPOSE 3000
62 |
63 | ENV PORT 3000
64 |
65 | # server.js is created by next build from the standalone output
66 | # https://nextjs.org/docs/pages/api-reference/next-config-js/output
67 | CMD HOSTNAME="0.0.0.0" node server.js
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 AI Roadmap Generator
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Roadmap AI
2 |
3 | 
4 |
5 | ## Introduction
6 |
7 | This project generates learning roadmaps for given search queries. For example, if a user searches for "machine learning", the project will generate a learning roadmap for machine learning. The roadmap will include the most important topics, resources, and learning paths for machine learning.
8 |
9 | ## Setting up the project locally
10 |
11 | - We use `pnpm` for package management. You can install `pnpm` by following the instructions [here](https://pnpm.io/installation).
12 | - To install the dependencies, run `pnpm install`.
13 | - To start the development server, run `pnpm dev`.
14 | - To build the project, run `pnpm build`.
15 | - To start the production server, run `pnpm start`.
16 |
17 | ## Features
18 |
19 | - Multimodel Support .
20 | - Share roadmap via public URL.
21 | - Recommended Orilley Books.
22 |
23 | ## Building and pusing the docker image to `ghcr.io` from the local machine
24 |
25 | ```bash
26 | docker login ghcr.io
27 | docker build . -t ghcr.io/thatbeautifuldream/ai-roadmap-generator:latest
28 | docker push ghcr.io/thatbeautifuldream/ai-roadmap-generator:latest
29 | ```
30 |
31 | ## Pulling and running docker image on the server
32 |
33 | ```bash
34 | docker pull ghcr.io/thatbeautifuldream/ai-roadmap-generator:latest
35 | docker run -d -p 3000:3000 --name ai-roadmap-generator ghcr.io/thatbeautifuldream/ai-roadmap-generator:latest
36 | ```
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/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": "slate",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils"
16 | }
17 | }
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | ai-roadmap-generator:
4 | image: ghcr.io/thatbeautifuldream/ai-roadmap-generator:latest
5 | container_name: ai-roadmap-generator
6 | ports:
7 | - 3000:3000
8 | restart: always
9 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | async redirects() {
4 | return [
5 | {
6 | source: "/twitter",
7 | destination: "https://twitter.com/airoadmapgen",
8 | permanent: true,
9 | },
10 | {
11 | source: "/discord",
12 | destination: "https://discord.gg/2rMV53UqYB",
13 | permanent: true,
14 | },
15 | ];
16 | },
17 | experimental: {
18 | typedRoutes: true,
19 | },
20 | output: "standalone",
21 | };
22 |
23 | export default nextConfig;
24 |
--------------------------------------------------------------------------------
/pfp_seed.js:
--------------------------------------------------------------------------------
1 | const { PrismaClient } = require("@prisma/client");
2 | const { clerkClient } = require("@clerk/nextjs");
3 |
4 | const prisma = new PrismaClient();
5 |
6 | async function updateImageUrls() {
7 | const dbUsers = await clerkClient.users.getUserList({
8 | limit: 250,
9 | });
10 |
11 | try {
12 | for (const dbUser of dbUsers) {
13 | if (dbUser.imageUrl) {
14 | await prisma.user.update({
15 | where: {
16 | id: dbUser.id,
17 | },
18 | data: {
19 | imageUrl: dbUser.imageUrl,
20 | },
21 | });
22 | console.log(`Updated image URL for user ${dbUser.firstName} ${dbUser.lastName}.`);
23 | }
24 | }
25 |
26 | console.log("Image URLs updated successfully.");
27 | } catch (error) {
28 | console.error("Error updating image URLs:", error);
29 | } finally {
30 | await prisma.$disconnect();
31 | }
32 | }
33 |
34 | (async () => {
35 | await updateImageUrls();
36 | })();
37 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/prisma/schema.prisma:
--------------------------------------------------------------------------------
1 | generator client {
2 | provider = "prisma-client-js"
3 | }
4 |
5 | datasource db {
6 | provider = "postgresql"
7 | url = env("DATABASE_URL")
8 | }
9 |
10 | model User {
11 | id String @id @unique
12 | name String
13 | email String
14 | imageUrl String?
15 | credits Int @default(5)
16 | roadmaps Roadmap[]
17 | savedRoadmaps SavedRoadmap[]
18 | }
19 |
20 | enum Visibility {
21 | PRIVATE
22 | PUBLIC
23 | }
24 |
25 | model Roadmap {
26 | id String @id @default(cuid())
27 | title String @unique
28 | content String
29 | userId String
30 | author User @relation(fields: [userId], references: [id], onDelete: Cascade)
31 | createdAt DateTime @default(now())
32 | views Int @default(0)
33 | searchCount Int @default(0)
34 | visibility Visibility @default(PUBLIC)
35 | drawerDetails DrawerDetail[]
36 |
37 | @@index([title])
38 | }
39 |
40 | model SavedRoadmap {
41 | id String @id @default(cuid())
42 | title String
43 | roadmapId String
44 | userId String
45 | author User @relation(fields: [userId], references: [id], onDelete: Cascade)
46 | }
47 |
48 | model DrawerDetail {
49 | id String @id @default(cuid())
50 | details String
51 | youtubeVideoIds String[]
52 | books String
53 | nodeName String @unique
54 | roadmapId String
55 | roadmap Roadmap @relation(fields: [roadmapId], references: [id], onDelete: Cascade)
56 | }
57 |
--------------------------------------------------------------------------------
/public/images/repository-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thatbeautifuldream/ai-roadmap-generator/7c70d72c405ce01b1856510d222e8b3da6519fc4/public/images/repository-screenshot.png
--------------------------------------------------------------------------------
/public/images/roadmapai-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thatbeautifuldream/ai-roadmap-generator/7c70d72c405ce01b1856510d222e8b3da6519fc4/public/images/roadmapai-screenshot.png
--------------------------------------------------------------------------------
/public/images/wikipedia.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thatbeautifuldream/ai-roadmap-generator/7c70d72c405ce01b1856510d222e8b3da6519fc4/public/images/wikipedia.png
--------------------------------------------------------------------------------
/public/opengraph-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thatbeautifuldream/ai-roadmap-generator/7c70d72c405ce01b1856510d222e8b3da6519fc4/public/opengraph-image.png
--------------------------------------------------------------------------------
/src/actions/users.ts:
--------------------------------------------------------------------------------
1 | "use server";
2 | import { db } from "@/lib/db";
3 | import { getUserId } from "./roadmaps";
4 |
5 | export const decrementCreditsByUserId = async () => {
6 | const userId = await getUserId();
7 | try {
8 | // Retrieve the current user's credits
9 | const user = await db.user.findUnique({
10 | where: {
11 | id: userId,
12 | },
13 | });
14 |
15 | // Check if user exists and has more than 0 credits
16 | if (user && user.credits > 0) {
17 | await db.user.update({
18 | where: {
19 | id: userId,
20 | },
21 | data: {
22 | credits: {
23 | decrement: 1, // use a positive number to indicate decrement
24 | },
25 | },
26 | });
27 | return true;
28 | }
29 | // Either user does not exist or does not have enough credits to decrement
30 | return false;
31 | } catch (e) {
32 | // Handle the error, optionally log it or throw it
33 | console.error(e);
34 | return false;
35 | }
36 | };
37 |
38 | export const userHasCredits = async () => {
39 | const userId = await getUserId();
40 | const user = await db.user.findUnique({
41 | where: {
42 | id: userId,
43 | },
44 | select: {
45 | credits: true,
46 | },
47 | });
48 |
49 | if (user && user?.credits > 0) {
50 | return true;
51 | }
52 | return false;
53 | };
54 |
55 | export const getUserCredits = async () => {
56 | const userId = await getUserId();
57 |
58 | if (!userId) {
59 | return;
60 | }
61 |
62 | const user = await db.user.findUnique({
63 | where: {
64 | id: userId,
65 | },
66 | select: {
67 | credits: true,
68 | },
69 | });
70 |
71 | return user?.credits;
72 | };
73 |
74 | export const getTotalUsers = async () => {
75 | const totalUsers = await db.user.count();
76 | return totalUsers;
77 | };
78 |
--------------------------------------------------------------------------------
/src/app/(auth)/layout.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | type Props = {
4 | children: React.ReactNode;
5 | };
6 |
7 | export default function AuthLayout(props: Props) {
8 | const { children } = props;
9 | return (
10 |
{text}
60 |{description}
11 |33 | No structured learning plan? No problem. 34 |
35 |36 | We help you generate a structured learning roadmap for any topic you 37 | want to learn. Just enter the topic and let us handle the rest. 38 |
39 |61 | It started from Vishwajeet generating roadmap tree from the AI and 62 | rendering React flow, rest of us liked the idea and built the bells 63 | and whistles around it. 64 |
65 |83 | {person.role} 84 |
85 |29 | 33 | 34 | WE ARE THE PRODUCT OF THE MONTH 🏆 THANK YOU! 35 | 36 | 37 |
38 |18 | We're working on a new version of our platform, expect new 19 | features and improvements soon! 20 |
21 |5 out of 5 stars
8 |16 |22 |17 | “100% AGREE! Also, the way team has launched the project is 18 | commendable. This is by far the most successful project launch on 19 | Peerlist” 20 |
21 |
{title}
76 |{item.text}
49 |