├── app ├── projects │ ├── components │ │ ├── questions-section.tsx │ │ ├── theme-provider.tsx │ │ ├── project-layout.tsx │ │ ├── project-page-provider.tsx │ │ ├── documents-section.tsx │ │ └── project-content.tsx │ └── [projectId] │ │ ├── layout.tsx │ │ ├── documents │ │ └── page.tsx │ │ ├── questions │ │ ├── components │ │ │ ├── index.ts │ │ │ ├── multi-step-response-handler.tsx │ │ │ ├── questions-states.tsx │ │ │ ├── questions-header.tsx │ │ │ ├── no-questions-available.tsx │ │ │ └── questions-filter-tabs.tsx │ │ └── page.tsx │ │ └── page.tsx ├── favicon.ico ├── error │ └── page.tsx ├── page.tsx ├── organizations │ └── [orgId] │ │ ├── layout.tsx │ │ ├── team │ │ └── page.tsx │ │ ├── settings │ │ └── page.tsx │ │ └── knowledge-base │ │ └── page.tsx ├── user │ └── actions.ts ├── logout │ └── page.tsx ├── login │ ├── confirmation │ │ └── page.tsx │ └── actions.ts ├── auth │ ├── callback │ │ └── route.ts │ └── confirm │ │ └── route.ts ├── api │ ├── generate-response │ │ └── route.ts │ ├── llamaparse │ │ └── route.ts │ ├── extract-questions │ │ └── route.ts │ ├── llamacloud │ │ ├── disconnect │ │ │ └── route.ts │ │ ├── connect │ │ │ └── route.ts │ │ ├── documents │ │ │ └── route.ts │ │ └── projects │ │ │ └── route.ts │ ├── questions │ │ └── [projectId] │ │ │ ├── route.ts │ │ │ ├── create │ │ │ └── route.ts │ │ │ └── answers │ │ │ ├── [questionId] │ │ │ └── route.ts │ │ │ └── route.ts │ ├── organizations │ │ ├── [id] │ │ │ ├── user-role │ │ │ │ └── route.ts │ │ │ └── members │ │ │ │ └── invite │ │ │ │ └── route.ts │ │ └── by-slug │ │ │ └── [slug] │ │ │ └── route.ts │ └── projects │ │ └── [projectId] │ │ └── timeline │ │ └── route.ts └── layout.tsx ├── lib ├── constants.ts ├── utils.ts ├── utils │ ├── supabase │ │ ├── client.ts │ │ ├── server.ts │ │ └── middleware.ts │ └── form-data-parser.ts ├── db.ts ├── interfaces │ ├── llama-index.ts │ ├── ai-service.ts │ ├── llamaparse-service.ts │ └── llamacloud-service.ts ├── env.ts ├── validators │ ├── generate-response.ts │ ├── extract-questions.ts │ └── llamaparse.ts ├── hooks │ └── use-api.ts ├── errors │ └── api-errors.ts ├── document-service.ts └── middleware │ └── api-handler.ts ├── public ├── llamaindex_logo.jpeg ├── vercel.svg ├── window.svg ├── file.svg ├── globe.svg └── next.svg ├── .cursor └── rules │ ├── directory-naming-conventions.mdc │ ├── next-js-15-state-management-rules.mdc │ ├── next-js-15-component-architecture-rules.mdc │ ├── next-js-15-async-request-api-rules.mdc │ └── general-typescript-and-react-rules.mdc ├── postcss.config.mjs ├── prisma └── migrations │ ├── 20250506053502_update_model_relationships │ └── migration.sql │ ├── 20250813183141_add_project_summary │ └── migration.sql │ ├── 20250813184727_add_project_eligibility │ └── migration.sql │ ├── 20250524003830_add_llamacloud_org_name │ └── migration.sql │ ├── migration_lock.toml │ ├── 20250523204248_add_llamacloud_integration │ └── migration.sql │ ├── 20250527001757_add_question_reference_id │ └── migration.sql │ ├── 20250717162429_remove_llamacloud_api_key │ └── migration.sql │ ├── 20250506052630_add_unique_constraint_to_answer │ └── migration.sql │ ├── 20250523211921_update_llamacloud_to_project_specific │ └── migration.sql │ ├── 20250507174516_add_sources │ └── migration.sql │ ├── 20250523215918_add_project_indexes │ └── migration.sql │ ├── 20250506052048_init │ └── migration.sql │ ├── 20250815193452_add_knowledge_base_models │ └── migration.sql │ └── 20250516203855_add_organizations_and_users │ └── migration.sql ├── next.config.ts ├── components ├── organizations │ ├── types.ts │ ├── EmptyProjectsState.tsx │ ├── index.ts │ ├── ProjectsHeader.tsx │ ├── TeamHeader.tsx │ ├── ProjectCard.tsx │ ├── ProjectsList.tsx │ ├── MemberTableRow.tsx │ ├── TeamMembersTable.tsx │ └── OrganizationContent.tsx ├── ui │ ├── aspect-ratio.tsx │ ├── skeleton.tsx │ ├── answer-display.tsx │ ├── markdown-renderer.tsx │ ├── sonner.tsx │ ├── label.tsx │ ├── separator.tsx │ ├── textarea.tsx │ ├── progress.tsx │ ├── toaster.tsx │ ├── collapsible.tsx │ ├── input.tsx │ ├── spinner.tsx │ ├── switch.tsx │ ├── avatar.tsx │ ├── checkbox.tsx │ ├── radio-group.tsx │ ├── hover-card.tsx │ ├── toggle.tsx │ ├── badge.tsx │ ├── popover.tsx │ ├── alert.tsx │ ├── scroll-area.tsx │ ├── tooltip.tsx │ ├── tabs.tsx │ ├── toggle-group.tsx │ ├── resizable.tsx │ ├── slider.tsx │ ├── accordion.tsx │ ├── card.tsx │ ├── button.tsx │ ├── delete-confirmation-dialog.tsx │ ├── input-otp.tsx │ ├── breadcrumb.tsx │ ├── table.tsx │ └── calendar.tsx ├── projects │ ├── PageSkeleton.tsx │ ├── ProjectCardSkeleton.tsx │ ├── SearchBar.tsx │ └── ProjectGrid.tsx ├── nav-secondary.tsx ├── nav-main.tsx ├── CTASection.tsx ├── upload │ ├── FileList.tsx │ └── UploadSection.tsx ├── StatsSection.tsx └── HeroSection.tsx ├── utils └── supabase │ └── client.ts ├── types ├── project.ts ├── organization.d.ts ├── organization.ts └── api.ts ├── components.json ├── providers └── providers.tsx ├── hooks └── use-mobile.ts ├── .gitignore ├── middleware.ts ├── tsconfig.json ├── next.config.js ├── LICENSE ├── tailwind.config.ts └── package.json /app/projects/components/questions-section.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/constants.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_LANGUAGE_MODEL = "gpt-4o-mini"; -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/run-llama/auto_rfp/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /public/llamaindex_logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/run-llama/auto_rfp/HEAD/public/llamaindex_logo.jpeg -------------------------------------------------------------------------------- /.cursor/rules/directory-naming-conventions.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: true 5 | --- 6 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: ["@tailwindcss/postcss"], 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /app/error/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | export default function ErrorPage() { 4 | return

Sorry, something went wrong

5 | } -------------------------------------------------------------------------------- /prisma/migrations/20250506053502_update_model_relationships/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropIndex 2 | DROP INDEX "answers_questionId_idx"; 3 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from "next/navigation"; 2 | 3 | export default function Home() { 4 | redirect("/organizations"); 5 | } 6 | -------------------------------------------------------------------------------- /prisma/migrations/20250813183141_add_project_summary/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "projects" ADD COLUMN "summary" TEXT; 3 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /prisma/migrations/20250813184727_add_project_eligibility/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "projects" ADD COLUMN "eligibility" TEXT[]; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20250524003830_add_llamacloud_org_name/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "organizations" ADD COLUMN "llamaCloudOrgName" TEXT; 3 | -------------------------------------------------------------------------------- /next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /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" 4 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /components/organizations/types.ts: -------------------------------------------------------------------------------- 1 | export interface TeamMember { 2 | id: string; 3 | name: string; 4 | email: string; 5 | role: 'owner' | 'admin' | 'member'; 6 | joinedAt: string; 7 | avatarUrl?: string; 8 | } -------------------------------------------------------------------------------- /prisma/migrations/20250523204248_add_llamacloud_integration/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "organizations" ADD COLUMN "llamaCloudApiKey" TEXT, 3 | ADD COLUMN "llamaCloudConnectedAt" TIMESTAMP(3), 4 | ADD COLUMN "llamaCloudOrgId" TEXT; 5 | -------------------------------------------------------------------------------- /utils/supabase/client.ts: -------------------------------------------------------------------------------- 1 | import { createBrowserClient } from '@supabase/ssr' 2 | 3 | export function createClient() { 4 | return createBrowserClient( 5 | process.env.NEXT_PUBLIC_SUPABASE_URL!, 6 | process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! 7 | ) 8 | } -------------------------------------------------------------------------------- /lib/utils/supabase/client.ts: -------------------------------------------------------------------------------- 1 | import { createBrowserClient } from '@supabase/ssr' 2 | 3 | export function createClient() { 4 | return createBrowserClient( 5 | process.env.NEXT_PUBLIC_SUPABASE_URL!, 6 | process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! 7 | ) 8 | } -------------------------------------------------------------------------------- /prisma/migrations/20250527001757_add_question_reference_id/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "questions" ADD COLUMN "referenceId" TEXT; 3 | 4 | -- CreateIndex 5 | CREATE INDEX "questions_projectId_referenceId_idx" ON "questions"("projectId", "referenceId"); 6 | -------------------------------------------------------------------------------- /app/projects/[projectId]/layout.tsx: -------------------------------------------------------------------------------- 1 | import { SidebarLayout } from "@/layouts/sidebar-layout/sidebar-layout"; 2 | import type { ReactNode } from "react"; 3 | 4 | export default function ProjectLayout({ children }: { children: ReactNode }) { 5 | return {children}; 6 | } -------------------------------------------------------------------------------- /prisma/migrations/20250717162429_remove_llamacloud_api_key/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `llamaCloudApiKey` on the `organizations` table. All the data in the column will be lost. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "organizations" DROP COLUMN "llamaCloudApiKey"; 9 | -------------------------------------------------------------------------------- /app/organizations/[orgId]/layout.tsx: -------------------------------------------------------------------------------- 1 | import { SidebarLayout } from "@/layouts/sidebar-layout/sidebar-layout"; 2 | 3 | export default function OrganizationsLayout({ 4 | children, 5 | }: { 6 | children: React.ReactNode; 7 | }) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } -------------------------------------------------------------------------------- /types/project.ts: -------------------------------------------------------------------------------- 1 | export interface Project { 2 | id: string; 3 | name: string; 4 | description?: string; 5 | summary?: string; // RFP summary generated from uploaded documents 6 | eligibility?: string[]; // Vendor eligibility requirements as bullet points 7 | createdAt: string; 8 | progress?: number; 9 | status?: string; 10 | } -------------------------------------------------------------------------------- /components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" 4 | 5 | function AspectRatio({ 6 | ...props 7 | }: React.ComponentProps) { 8 | return 9 | } 10 | 11 | export { AspectRatio } 12 | -------------------------------------------------------------------------------- /components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | function Skeleton({ className, ...props }: React.ComponentProps<"div">) { 4 | return ( 5 |
10 | ) 11 | } 12 | 13 | export { Skeleton } 14 | -------------------------------------------------------------------------------- /prisma/migrations/20250506052630_add_unique_constraint_to_answer/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - A unique constraint covering the columns `[questionId]` on the table `answers` will be added. If there are existing duplicate values, this will fail. 5 | 6 | */ 7 | -- CreateIndex 8 | CREATE UNIQUE INDEX "answers_questionId_key" ON "answers"("questionId"); 9 | -------------------------------------------------------------------------------- /app/projects/components/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import * as React from 'react' 4 | import { 5 | ThemeProvider as NextThemesProvider, 6 | type ThemeProviderProps, 7 | } from 'next-themes' 8 | 9 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) { 10 | return {children} 11 | } 12 | -------------------------------------------------------------------------------- /public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/projects/components/project-layout.tsx: -------------------------------------------------------------------------------- 1 | import type React from "react" 2 | 3 | export function ProjectLayout({ children }: { children: React.ReactNode }) { 4 | return ( 5 |
6 |
7 |
{children}
8 |
9 |
10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /prisma/migrations/20250523211921_update_llamacloud_to_project_specific/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `llamaCloudOrgId` on the `organizations` table. All the data in the column will be lost. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "organizations" DROP COLUMN "llamaCloudOrgId", 9 | ADD COLUMN "llamaCloudProjectId" TEXT, 10 | ADD COLUMN "llamaCloudProjectName" TEXT; 11 | -------------------------------------------------------------------------------- /app/projects/[projectId]/documents/page.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { DocumentsSection } from "../../components/documents-section"; 3 | 4 | interface DocumentsPageProps { 5 | params: Promise<{ projectId: string }>; 6 | } 7 | 8 | export default async function DocumentsPage({ params }: DocumentsPageProps) { 9 | const { projectId } = await params; 10 | 11 | return ; 12 | } -------------------------------------------------------------------------------- /components/ui/answer-display.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import { MarkdownRenderer } from "./markdown-renderer"; 5 | 6 | interface AnswerDisplayProps { 7 | content: string; 8 | className?: string; 9 | } 10 | 11 | export function AnswerDisplay({ content, className = "" }: AnswerDisplayProps) { 12 | return ( 13 |
14 | 15 |
16 | ); 17 | } -------------------------------------------------------------------------------- /components/ui/markdown-renderer.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import ReactMarkdown from "react-markdown"; 5 | 6 | interface MarkdownRendererProps { 7 | content: string; 8 | className?: string; 9 | } 10 | 11 | export function MarkdownRenderer({ content, className = "" }: MarkdownRendererProps) { 12 | return ( 13 |
14 | {content} 15 |
16 | ); 17 | } -------------------------------------------------------------------------------- /.cursor/rules/next-js-15-state-management-rules.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: true 5 | --- 6 | --- 7 | description: Defines the recommended state management strategies for Next.js 15 applications, including server and client contexts. 8 | globs: app/**/* 9 | --- 10 | - Use `useActionState` instead of deprecated `useFormState`. 11 | - Leverage enhanced `useFormStatus` with new properties (data, method, action). 12 | - Implement URL state management with 'nuqs'. 13 | - Minimize client-side state. -------------------------------------------------------------------------------- /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": "", 8 | "css": "app/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } -------------------------------------------------------------------------------- /lib/db.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | 3 | // PrismaClient is attached to the `global` object in development to prevent 4 | // exhausting your database connection limit. 5 | const globalForPrisma = global as unknown as { prisma: PrismaClient }; 6 | 7 | export const db = 8 | globalForPrisma.prisma || 9 | new PrismaClient({ 10 | log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'], 11 | }); 12 | 13 | if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db; -------------------------------------------------------------------------------- /providers/providers.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { ThemeProvider } from "next-themes"; 4 | import { ReactNode } from "react"; 5 | import { OrganizationProvider } from "@/context/organization-context"; 6 | 7 | export function Providers({ children }: { children: ReactNode }) { 8 | return ( 9 | 15 | 16 | {children} 17 | 18 | 19 | ); 20 | } -------------------------------------------------------------------------------- /.cursor/rules/next-js-15-component-architecture-rules.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: true 5 | --- 6 | --- 7 | description: Specifies the best practices for building React components within the Next.js 15 App Router structure. 8 | globs: app/**/* 9 | --- 10 | - Favor React Server Components (RSC) where possible. 11 | - Minimize 'use client' directives. 12 | - Implement proper error boundaries. 13 | - Use Suspense for async operations. 14 | - Optimize for performance and Web Vitals. 15 | - Use Shadcn UI whenever there is a component available for the UI goal 16 | -------------------------------------------------------------------------------- /app/organizations/[orgId]/team/page.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from "react"; 2 | import { TeamContent } from "@/components/organizations/TeamContent"; 3 | import { PageSkeleton } from "@/components/projects/PageSkeleton"; 4 | 5 | interface TeamPageProps { 6 | params: Promise<{ 7 | orgId: string; 8 | }>; 9 | } 10 | 11 | export default async function TeamPage({ params }: TeamPageProps) { 12 | const { orgId } = await params; 13 | 14 | return ( 15 | }> 16 | 17 | 18 | ); 19 | } -------------------------------------------------------------------------------- /app/user/actions.ts: -------------------------------------------------------------------------------- 1 | 'use server'; 2 | 3 | import { createClient } from '@/lib/utils/supabase/server'; 4 | 5 | export async function getCurrentUserEmail(): Promise { 6 | try { 7 | const supabase = await createClient(); 8 | const { data, error } = await supabase.auth.getUser(); 9 | 10 | if (error) { 11 | console.error('Error fetching user:', error); 12 | return null; 13 | } 14 | 15 | return data.user?.email || null; 16 | } catch (e) { 17 | console.error('Exception fetching user email:', e); 18 | return null; 19 | } 20 | } -------------------------------------------------------------------------------- /app/organizations/[orgId]/settings/page.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from "react"; 2 | import { SettingsContent } from "@/components/organizations/SettingsContent"; 3 | import { PageSkeleton } from "@/components/projects/PageSkeleton"; 4 | 5 | interface SettingsPageProps { 6 | params: Promise<{ 7 | orgId: string; 8 | }>; 9 | } 10 | 11 | export default async function SettingsPage({ params }: SettingsPageProps) { 12 | const { orgId } = await params; 13 | 14 | return ( 15 | }> 16 | 17 | 18 | ); 19 | } -------------------------------------------------------------------------------- /app/logout/page.tsx: -------------------------------------------------------------------------------- 1 | import { logout } from '@/app/login/actions' 2 | 3 | export default function LogoutPage() { 4 | return ( 5 |
6 |

Log out

7 |

Are you sure you want to log out?

8 |
9 | 15 |
16 |
17 | ) 18 | } -------------------------------------------------------------------------------- /.cursor/rules/next-js-15-async-request-api-rules.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: true 5 | --- 6 | --- 7 | description: Dictates how asynchronous requests should be handled within Next.js 15, specifically concerning runtime APIs. 8 | globs: app/**/* 9 | --- 10 | - Always use async versions of runtime APIs: 11 | typescript 12 | const cookieStore = await cookies() 13 | const headersList = await headers() 14 | const { isEnabled } = await draftMode() 15 | 16 | - Handle async params in layouts/pages: 17 | typescript 18 | const params = await props.params 19 | const searchParams = await props.searchParams -------------------------------------------------------------------------------- /app/login/confirmation/page.tsx: -------------------------------------------------------------------------------- 1 | export default function ConfirmationPage() { 2 | return ( 3 |
4 |

Check your email

5 |
6 |

7 | We've sent you a magic link to your email address. Click the link in the email to sign in. 8 |

9 |

10 | If you don't see the email, check your spam folder. The link will expire after 24 hours. 11 |

12 |
13 |
14 | ) 15 | } -------------------------------------------------------------------------------- /hooks/use-mobile.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | const MOBILE_BREAKPOINT = 768 4 | 5 | export function useIsMobile() { 6 | const [isMobile, setIsMobile] = React.useState(undefined) 7 | 8 | React.useEffect(() => { 9 | const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) 10 | const onChange = () => { 11 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) 12 | } 13 | mql.addEventListener("change", onChange) 14 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) 15 | return () => mql.removeEventListener("change", onChange) 16 | }, []) 17 | 18 | return !!isMobile 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | CLAUDE.md 43 | -------------------------------------------------------------------------------- /middleware.ts: -------------------------------------------------------------------------------- 1 | import { type NextRequest } from 'next/server' 2 | import { updateSession } from '@/lib/utils/supabase/middleware' 3 | 4 | export async function middleware(request: NextRequest) { 5 | return await updateSession(request) 6 | } 7 | 8 | export const config = { 9 | matcher: [ 10 | /* 11 | * Match all request paths except for the ones starting with: 12 | * - _next/static (static files) 13 | * - _next/image (image optimization files) 14 | * - favicon.ico (favicon file) 15 | * Feel free to modify this pattern to include more paths. 16 | */ 17 | '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)', 18 | ], 19 | } -------------------------------------------------------------------------------- /components/ui/sonner.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useTheme } from "next-themes" 4 | import { Toaster as Sonner, ToasterProps } from "sonner" 5 | 6 | const Toaster = ({ ...props }: ToasterProps) => { 7 | const { theme = "system" } = useTheme() 8 | 9 | return ( 10 | 22 | ) 23 | } 24 | 25 | export { Toaster } 26 | -------------------------------------------------------------------------------- /types/organization.d.ts: -------------------------------------------------------------------------------- 1 | import { Project } from './project'; 2 | 3 | export interface Organization { 4 | id: string; 5 | name: string; 6 | slug: string; 7 | description?: string; 8 | createdAt: string; 9 | updatedAt: string; 10 | projects?: Project[]; 11 | organizationUsers?: OrganizationUser[]; 12 | } 13 | 14 | export interface OrganizationUser { 15 | id: string; 16 | role: 'owner' | 'admin' | 'member'; 17 | createdAt: string; 18 | updatedAt: string; 19 | userId: string; 20 | organizationId: string; 21 | user?: User; 22 | } 23 | 24 | export interface User { 25 | id: string; 26 | email: string; 27 | name?: string; 28 | createdAt: string; 29 | updatedAt: string; 30 | } -------------------------------------------------------------------------------- /types/organization.ts: -------------------------------------------------------------------------------- 1 | import { Project } from './project'; 2 | 3 | export interface Organization { 4 | id: string; 5 | name: string; 6 | slug: string; 7 | description?: string; 8 | createdAt: string; 9 | updatedAt: string; 10 | projects?: Project[]; 11 | organizationUsers?: OrganizationUser[]; 12 | } 13 | 14 | export interface OrganizationUser { 15 | id: string; 16 | role: 'owner' | 'admin' | 'member'; 17 | createdAt: string; 18 | updatedAt: string; 19 | userId: string; 20 | organizationId: string; 21 | user?: User; 22 | } 23 | 24 | export interface User { 25 | id: string; 26 | email: string; 27 | name?: string; 28 | createdAt: string; 29 | updatedAt: string; 30 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | function Label({ 9 | className, 10 | ...props 11 | }: React.ComponentProps) { 12 | return ( 13 | 21 | ) 22 | } 23 | 24 | export { Label } 25 | -------------------------------------------------------------------------------- /prisma/migrations/20250507174516_add_sources/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "sources" ( 3 | "id" TEXT NOT NULL, 4 | "fileName" TEXT NOT NULL, 5 | "filePath" TEXT, 6 | "pageNumber" TEXT, 7 | "documentId" TEXT, 8 | "relevance" INTEGER, 9 | "textContent" TEXT, 10 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 11 | "answerId" TEXT NOT NULL, 12 | 13 | CONSTRAINT "sources_pkey" PRIMARY KEY ("id") 14 | ); 15 | 16 | -- CreateIndex 17 | CREATE INDEX "sources_answerId_idx" ON "sources"("answerId"); 18 | 19 | -- AddForeignKey 20 | ALTER TABLE "sources" ADD CONSTRAINT "sources_answerId_fkey" FOREIGN KEY ("answerId") REFERENCES "answers"("id") ON DELETE CASCADE ON UPDATE CASCADE; 21 | -------------------------------------------------------------------------------- /app/projects/components/project-page-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React, { useState } from "react"; 4 | import { SidebarProvider } from "@/components/ui/sidebar"; 5 | 6 | interface ProjectPageProviderProps { 7 | projectId: string; 8 | children: React.ReactNode; 9 | } 10 | 11 | export function ProjectPageProvider({ projectId, children }: ProjectPageProviderProps) { 12 | // Control sidebar state at this level to avoid re-renders causing toggle issues 13 | const [sidebarOpen, setSidebarOpen] = useState(true); 14 | 15 | return ( 16 | 21 | {children} 22 | 23 | ); 24 | } -------------------------------------------------------------------------------- /components/organizations/EmptyProjectsState.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import { Button } from '@/components/ui/button'; 5 | import { PlusIcon } from 'lucide-react'; 6 | 7 | interface EmptyProjectsStateProps { 8 | onCreateProject: () => void; 9 | } 10 | 11 | export function EmptyProjectsState({ onCreateProject }: EmptyProjectsStateProps) { 12 | return ( 13 |
14 |

No projects found

15 | 22 |
23 | ); 24 | } -------------------------------------------------------------------------------- /app/organizations/[orgId]/knowledge-base/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Suspense } from 'react'; 4 | import { PageSkeleton } from '@/components/projects/PageSkeleton'; 5 | import { KnowledgeBaseContent } from '@/components/organizations/KnowledgeBaseContent'; 6 | 7 | interface KnowledgeBasePageProps { 8 | params: Promise<{ 9 | orgId: string; 10 | }>; 11 | } 12 | 13 | export default function KnowledgeBasePage({ params }: KnowledgeBasePageProps) { 14 | return ( 15 | }> 16 | 17 | 18 | ); 19 | } 20 | 21 | function KnowledgeBasePageContent({ params }: KnowledgeBasePageProps) { 22 | return ; 23 | } 24 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | // Configure environment variables that will be available on both server and client side 4 | env: { 5 | // You will need to set LLAMACLOUD_API_KEY in your deployment environment 6 | // or in a .env file that's not committed to version control 7 | LLAMACLOUD_API_KEY: process.env.LLAMACLOUD_API_KEY, 8 | // Internal API key for internal users 9 | LLAMACLOUD_API_KEY_INTERNAL: process.env.LLAMACLOUD_API_KEY_INTERNAL, 10 | // Internal email domain (defaults to @runllama.ai) 11 | INTERNAL_EMAIL_DOMAIN: process.env.INTERNAL_EMAIL_DOMAIN, 12 | }, 13 | // Other Next.js config options 14 | reactStrictMode: true, 15 | } 16 | 17 | module.exports = nextConfig; -------------------------------------------------------------------------------- /app/auth/callback/route.ts: -------------------------------------------------------------------------------- 1 | import { type NextRequest } from 'next/server' 2 | import { createClient } from '@/lib/utils/supabase/server' 3 | import { redirect } from 'next/navigation' 4 | 5 | export async function GET(request: NextRequest) { 6 | const { searchParams } = new URL(request.url) 7 | const code = searchParams.get('code') 8 | 9 | if (!code) { 10 | return redirect('/login') 11 | } 12 | 13 | const supabase = await createClient() 14 | 15 | // Exchange the code for a session 16 | const { error } = await supabase.auth.exchangeCodeForSession(code) 17 | 18 | if (error) { 19 | console.error('Error exchanging code for session:', error) 20 | return redirect('/error') 21 | } 22 | 23 | // Successful authentication, redirect to home 24 | return redirect('/') 25 | } -------------------------------------------------------------------------------- /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 | function Separator({ 9 | className, 10 | orientation = "horizontal", 11 | decorative = true, 12 | ...props 13 | }: React.ComponentProps) { 14 | return ( 15 | 25 | ) 26 | } 27 | 28 | export { Separator } 29 | -------------------------------------------------------------------------------- /components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | function Textarea({ className, ...props }: React.ComponentProps<"textarea">) { 6 | return ( 7 |