├── .eslintrc.json ├── .gitignore ├── .vscode └── settings.json ├── README.md ├── app ├── api │ └── generate-report │ │ └── route.ts ├── favicon.ico ├── fonts │ ├── GeistMonoVF.woff │ └── GeistVF.woff ├── globals.css ├── layout.tsx └── page.tsx ├── bun.lockb ├── components.json ├── components ├── consultant-report-generator.tsx └── ui │ ├── button.tsx │ ├── input.tsx │ └── textarea.tsx ├── lib └── utils.ts ├── next.config.mjs ├── package.json ├── postcss.config.mjs ├── public ├── file-text.svg ├── globe.svg ├── next.svg ├── vercel.svg └── window.svg ├── tailwind.config.ts └── tsconfig.json /.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 | # env files (can opt-in for commiting if needed) 29 | .env* 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dotenv.enableAutocloaking": true 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Code Documentation 3 | 4 | ## Overview 5 | 6 | This project is a Next.js application that implements a Consultant Report Generator. It allows users to generate reports based on URLs and custom instructions, as well as edit and format the generated content. 7 | 8 | ## Key Components 9 | 10 | ### 1. ConsultantReportGeneratorComponent (components/consultant-report-generator.tsx) 11 | 12 | This is the main component of the application. It includes: 13 | 14 | - A form for URL and instruction input 15 | - A rich text editor for report content 16 | - Formatting controls for the editor 17 | - A function to generate random sample reports 18 | 19 | #### Inputs: 20 | - URL (string) 21 | - Instructions (string) 22 | 23 | #### Outputs: 24 | - Generated report content (HTML) 25 | 26 | ### 2. API Route (app/api/generate-report/route.ts) 27 | 28 | This serverless function handles the report generation process: 29 | 30 | - Crawls the provided URL using Firecrawl 31 | - Generates a report using OpenAI's GPT model 32 | 33 | #### Inputs: 34 | - URL (string) 35 | - Instructions (string) 36 | 37 | #### Outputs: 38 | - Generated HTML content 39 | - Crawl data 40 | 41 | ### 3. UI Components (components/ui/*) 42 | 43 | Custom UI components used throughout the application: 44 | - Button 45 | - Input 46 | - Textarea 47 | 48 | ### 4. Layout and Styling 49 | 50 | - Global styles defined in app/globals.css 51 | - Tailwind CSS used for styling 52 | - Custom font configuration in app/layout.tsx 53 | 54 | ## Key Features 55 | 56 | 1. URL-based report generation 57 | 2. Rich text editing capabilities 58 | 3. Random sample report generation 59 | 4. Responsive design 60 | 61 | ## Technologies Used 62 | 63 | - Next.js (React framework) 64 | - TypeScript 65 | - Tailwind CSS 66 | - TipTap (rich text editor) 67 | - OpenAI API 68 | - Firecrawl (web crawling) 69 | 70 | ## Setup and Usage 71 | 72 | 1. Install dependencies: `npm install` 73 | 2. Set up environment variables (OpenAI API key, Firecrawl API key) 74 | 3. Run the development server: `npm run dev` 75 | 4. Access the application at `http://localhost:3000` 76 | 77 | ## Deployment 78 | 79 | The project is configured for easy deployment on Vercel, the creators of Next.js. 80 | -------------------------------------------------------------------------------- /app/api/generate-report/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | import OpenAI from "openai"; 3 | import FirecrawlApp from '@mendable/firecrawl-js'; 4 | 5 | const openai = new OpenAI(); 6 | const firecrawlApp = new FirecrawlApp({ apiKey: process.env.FIRECRAWL_API_KEY }); 7 | 8 | export async function POST(request: Request) { 9 | const { url, instructions } = await request.json(); 10 | 11 | const crawlResponse = await firecrawlApp.crawlUrl(url, { 12 | limit: 5, 13 | scrapeOptions: { 14 | formats: ['markdown', 'html'], 15 | } 16 | }); 17 | 18 | if (!crawlResponse.success) { 19 | throw new Error(`Failed to crawl: ${crawlResponse.error}`); 20 | } 21 | 22 | const completion = await openai.chat.completions.create({ 23 | model: "o1-preview", 24 | messages: [ 25 | { 26 | role: "user", 27 | content: ` 28 | Generate a report with the following details in valid HTML: 29 | - Instructions: ${instructions} 30 | - URL: ${url} 31 | - Results: ${JSON.stringify(crawlResponse.data)} 32 | ` 33 | } 34 | ] 35 | }); 36 | 37 | const generatedHtml = completion.choices[0].message.content; 38 | 39 | return NextResponse.json({ html: generatedHtml, crawlData: crawlResponse.data }); 40 | } -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developersdigest/ai-consulting/7a7622c3921b77de0f0de90682467c615b3f8c11/app/favicon.ico -------------------------------------------------------------------------------- /app/fonts/GeistMonoVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developersdigest/ai-consulting/7a7622c3921b77de0f0de90682467c615b3f8c11/app/fonts/GeistMonoVF.woff -------------------------------------------------------------------------------- /app/fonts/GeistVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developersdigest/ai-consulting/7a7622c3921b77de0f0de90682467c615b3f8c11/app/fonts/GeistVF.woff -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --background: #ffffff; 7 | --foreground: #171717; 8 | } 9 | 10 | @media (prefers-color-scheme: dark) { 11 | :root { 12 | --background: #0a0a0a; 13 | --foreground: #ededed; 14 | } 15 | } 16 | 17 | body { 18 | color: var(--foreground); 19 | background: var(--background); 20 | font-synthesis: none; 21 | } 22 | 23 | @layer base { 24 | :root { 25 | --background: 0 0% 100%; 26 | --foreground: 0 0% 3.9%; 27 | --card: 0 0% 100%; 28 | --card-foreground: 0 0% 3.9%; 29 | --popover: 0 0% 100%; 30 | --popover-foreground: 0 0% 3.9%; 31 | --primary: 0 0% 9%; 32 | --primary-foreground: 0 0% 98%; 33 | --secondary: 0 0% 96.1%; 34 | --secondary-foreground: 0 0% 9%; 35 | --muted: 0 0% 96.1%; 36 | --muted-foreground: 0 0% 45.1%; 37 | --accent: 0 0% 96.1%; 38 | --accent-foreground: 0 0% 9%; 39 | --destructive: 0 84.2% 60.2%; 40 | --destructive-foreground: 0 0% 98%; 41 | --border: 0 0% 89.8%; 42 | --input: 0 0% 89.8%; 43 | --ring: 0 0% 3.9%; 44 | --chart-1: 12 76% 61%; 45 | --chart-2: 173 58% 39%; 46 | --chart-3: 197 37% 24%; 47 | --chart-4: 43 74% 66%; 48 | --chart-5: 27 87% 67%; 49 | --radius: 0.5rem; 50 | } 51 | .dark { 52 | --background: 0 0% 3.9%; 53 | --foreground: 0 0% 98%; 54 | --card: 0 0% 3.9%; 55 | --card-foreground: 0 0% 98%; 56 | --popover: 0 0% 3.9%; 57 | --popover-foreground: 0 0% 98%; 58 | --primary: 0 0% 98%; 59 | --primary-foreground: 0 0% 9%; 60 | --secondary: 0 0% 14.9%; 61 | --secondary-foreground: 0 0% 98%; 62 | --muted: 0 0% 14.9%; 63 | --muted-foreground: 0 0% 63.9%; 64 | --accent: 0 0% 14.9%; 65 | --accent-foreground: 0 0% 98%; 66 | --destructive: 0 62.8% 30.6%; 67 | --destructive-foreground: 0 0% 98%; 68 | --border: 0 0% 14.9%; 69 | --input: 0 0% 14.9%; 70 | --ring: 0 0% 83.1%; 71 | --chart-1: 220 70% 50%; 72 | --chart-2: 160 60% 45%; 73 | --chart-3: 30 80% 55%; 74 | --chart-4: 280 65% 60%; 75 | --chart-5: 340 75% 55%; 76 | } 77 | } 78 | 79 | @layer base { 80 | * { 81 | @apply border-border; 82 | } 83 | body { 84 | @apply bg-background text-foreground; 85 | } 86 | } 87 | 88 | 89 | .tiptap { 90 | font-family: 'Arial', sans-serif; 91 | line-height: 1.6; 92 | color: var(--foreground); 93 | } 94 | 95 | .tiptap h1, .tiptap h2, .tiptap h3, .tiptap h4, .tiptap h5, .tiptap h6 { 96 | font-weight: bold; 97 | margin-top: 1.5em; 98 | margin-bottom: 0.5em; 99 | color: var(--primary-foreground); 100 | } 101 | 102 | .tiptap p { 103 | margin-bottom: 1em; 104 | } 105 | 106 | .tiptap ul, .tiptap ol { 107 | margin: 1em 0; 108 | padding-left: 2em; 109 | } 110 | 111 | .tiptap ul li, .tiptap ol li { 112 | margin-bottom: 0.5em; 113 | } 114 | 115 | .tiptap strong { 116 | font-weight: bold; 117 | color: var(--primary); 118 | } 119 | 120 | .tiptap em { 121 | font-style: italic; 122 | color: var(--secondary); 123 | } 124 | 125 | .tiptap u { 126 | text-decoration: underline; 127 | color: var(--accent); 128 | } 129 | 130 | .tiptap blockquote { 131 | border-left: 4px solid var(--muted); 132 | padding-left: 1em; 133 | color: var(--muted-foreground); 134 | margin: 1em 0; 135 | } 136 | 137 | .tiptap a { 138 | color: var(--chart-1); 139 | text-decoration: underline; 140 | } 141 | 142 | .tiptap img { 143 | max-width: 100%; 144 | height: auto; 145 | margin: 1em 0; 146 | } 147 | 148 | .tiptap table { 149 | width: 100%; 150 | border-collapse: collapse; 151 | margin: 1em 0; 152 | } 153 | 154 | .tiptap th, .tiptap td { 155 | border: 1px solid var(--border); 156 | padding: 0.5em; 157 | text-align: left; 158 | } 159 | 160 | .tiptap th { 161 | background-color: var(--muted); 162 | color: var(--muted-foreground); 163 | } 164 | 165 | .tiptap code { 166 | font-family: 'Courier New', monospace; 167 | background-color: var(--input); 168 | padding: 0.2em 0.4em; 169 | border-radius: var(--radius); 170 | } 171 | 172 | .tiptap pre { 173 | background-color: var(--input); 174 | padding: 1em; 175 | border-radius: var(--radius); 176 | overflow-x: auto; 177 | } 178 | 179 | .tiptap hr { 180 | border: none; 181 | border-top: 1px solid var(--border); 182 | margin: 2em 0; 183 | } 184 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import localFont from "next/font/local"; 3 | import "./globals.css"; 4 | 5 | const geistSans = localFont({ 6 | src: "./fonts/GeistVF.woff", 7 | variable: "--font-geist-sans", 8 | }); 9 | const geistMono = localFont({ 10 | src: "./fonts/GeistMonoVF.woff", 11 | variable: "--font-geist-mono", 12 | }); 13 | 14 | export const metadata: Metadata = { 15 | title: "Create Next App", 16 | description: "Generated by create next app", 17 | }; 18 | 19 | export default function RootLayout({ 20 | children, 21 | }: Readonly<{ 22 | children: React.ReactNode; 23 | }>) { 24 | return ( 25 | 26 |
27 | {children} 28 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | import { ConsultantReportGeneratorComponent } from "@/components/consultant-report-generator"; 2 | 3 | export default function Home() { 4 | return ( 5 |Our analysis indicates a 25% growth potential in emerging markets. Key recommendations:
16 |By following these strategies, we project a 15% increase in global market share within the next fiscal year.
`, 22 | 23 | `Proposed 3-Phase Implementation Plan
25 |Expected outcomes include a 30% increase in operational efficiency and improved customer satisfaction scores.
`, 31 | 32 | `To align with global environmental goals and enhance brand reputation, we recommend:
34 |These initiatives are projected to result in a 15% reduction in carbon footprint and potentially unlock new market segments.
`, 40 | 41 | `Based on our recent survey, we've identified key areas for improvement:
43 |Target: Increase employee satisfaction scores by 20% within 6 months
`, 49 | 50 | `Our analysis reveals potential for significant efficiency gains:
52 |Estimated annual savings: $2.5M
` 58 | ] 59 | 60 | export function ConsultantReportGeneratorComponent() { 61 | const [reportContent, setReportContent] = useState('') 62 | const [url, setUrl] = useState('') 63 | const [instructions, setInstructions] = useState('') 64 | const [loading, setLoading] = useState(false) 65 | 66 | const editor = useEditor({ 67 | extensions: [ 68 | StarterKit, 69 | Underline, 70 | TextAlign.configure({ types: ['heading', 'paragraph'] }), 71 | ], 72 | content: reportContent, 73 | onUpdate: ({ editor }) => { 74 | setReportContent(editor.getHTML()) 75 | }, 76 | }) 77 | 78 | const generateRandomReport = () => { 79 | const randomReport = sampleReports[Math.floor(Math.random() * sampleReports.length)] 80 | editor?.commands.setContent(randomReport) 81 | } 82 | 83 | const handleSubmit = async (e: React.FormEvent) => { 84 | e.preventDefault() 85 | setLoading(true) 86 | const response = await fetch('/api/generate-report', { 87 | method: 'POST', 88 | headers: { 89 | 'Content-Type': 'application/json', 90 | }, 91 | body: JSON.stringify({ url, instructions }), 92 | }) 93 | const data = await response.json() 94 | editor?.commands.setContent(data.html) 95 | setLoading(false) 96 | } 97 | 98 | return ( 99 |