From de360a574e615258d7abaae3b33fa181664ee36f Mon Sep 17 00:00:00 2001 From: "tejas.prakash" Date: Thu, 21 Aug 2025 10:32:23 +0530 Subject: [PATCH] Updated codenuk --- src/app/api/ai/analyze/route.ts | 99 ++- src/components/ai/AICustomFeatureCreator.tsx | 107 ++-- src/components/main-dashboard.tsx | 603 +++++++++++++++++-- src/lib/template-service.ts | 42 +- src/services/aiAnalysis.ts | 66 +- 5 files changed, 750 insertions(+), 167 deletions(-) diff --git a/src/app/api/ai/analyze/route.ts b/src/app/api/ai/analyze/route.ts index f5659f4..f876b2d 100644 --- a/src/app/api/ai/analyze/route.ts +++ b/src/app/api/ai/analyze/route.ts @@ -1,30 +1,86 @@ -import { NextResponse } from 'next/server' +import { NextRequest, NextResponse } from 'next/server' +import Anthropic from '@anthropic-ai/sdk' -// Proxy AI analysis to the Requirement Processor backend -// Expects body: { description: string, project_type?: string, feature_name?: string, requirements?: string[] } -export async function POST(request: Request) { +export const runtime = 'nodejs' + +export async function POST(req: NextRequest) { try { - const body = await request.json() - const requirementProcessorUrl = process.env.REQUIREMENT_PROCESSOR_URL || 'http://localhost:8001' + const body = await req.json() + const featureName: string = body.featureName || 'Custom Feature' + const description: string = body.description || '' + const requirements: string[] = Array.isArray(body.requirements) ? body.requirements : [] + const projectType: string | undefined = body.projectType - const resp = await fetch(`${requirementProcessorUrl}/api/v1/analyze-feature`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - description: body.description, - project_type: body.project_type, - feature_name: body.feature_name, - requirements: Array.isArray(body.requirements) ? body.requirements : [], - }), - }) + const apiKey = + process.env.ANTHROPIC_API_KEY || + process.env.REACT_APP_ANTHROPIC_API_KEY || + process.env.NEXT_PUBLIC_ANTHROPIC_API_KEY || + '' - if (!resp.ok) { - const text = await resp.text() - return NextResponse.json({ success: false, message: `Upstream error: ${text}` }, { status: 500 }) + if (!apiKey) { + return NextResponse.json( + { success: false, message: 'Missing Anthropic API key in env' }, + { status: 500 } + ) } - const data = await resp.json() - return NextResponse.json({ success: true, data }) + const anthropic = new Anthropic({ apiKey }) + + const requirementsText = (requirements || []) + .filter((r) => r && r.trim()) + .map((r) => `- ${r}`) + .join('\n') + + const prompt = ` +Analyze this feature and provide complexity assessment and business logic rules: + +Project Type: ${projectType || 'Generic'} +Feature Name: ${featureName} +Description: ${description} + +Requirements: +${requirementsText} + +Based on these requirements, provide a JSON response with: +1. Complexity level (low/medium/high) +2. Business logic rules that should be implemented + +Complexity Guidelines: +- LOW: Simple CRUD operations, basic display features +- MEDIUM: Moderate business logic, some validations, basic integrations +- HIGH: Complex business rules, security requirements, external integrations, compliance needs + +Return ONLY a JSON object in this exact format: +{ + "complexity": "low|medium|high", + "logicRules": [ + "Business rule 1 based on requirements", + "Business rule 2 based on requirements", + "Business rule 3 based on requirements" + ] +}` + + const message = await anthropic.messages.create({ + model: 'claude-3-5-sonnet-20241022', + max_tokens: 1000, + temperature: 0.2, + messages: [{ role: 'user', content: prompt }], + }) + + const responseText = (message as any).content?.[0]?.text?.trim?.() || '' + const jsonMatch = responseText.match(/\{[\s\S]*\}/) + if (!jsonMatch) { + return NextResponse.json( + { success: false, message: 'Invalid AI response format' }, + { status: 502 } + ) + } + + const parsed = JSON.parse(jsonMatch[0]) + const complexity = (parsed.complexity as 'low' | 'medium' | 'high') || 'medium' + const logicRules = Array.isArray(parsed.logicRules) ? parsed.logicRules : [] + + return NextResponse.json({ success: true, data: { complexity, logicRules } }) } catch (error: any) { return NextResponse.json( { success: false, message: error?.message || 'AI analysis failed' }, @@ -33,4 +89,3 @@ export async function POST(request: Request) { } } - diff --git a/src/components/ai/AICustomFeatureCreator.tsx b/src/components/ai/AICustomFeatureCreator.tsx index 2401a5f..29c47da 100644 --- a/src/components/ai/AICustomFeatureCreator.tsx +++ b/src/components/ai/AICustomFeatureCreator.tsx @@ -37,43 +37,36 @@ export function AICustomFeatureCreator({ const [requirements, setRequirements] = useState>([ { text: '', rules: [] }, ]) + const [logicRules, setLogicRules] = useState([]) const handleAnalyze = async () => { if (!featureDescription.trim() && requirements.every(r => !r.text.trim())) return setIsAnalyzing(true) setAnalysisError(null) try { - // 1) Analyze overall description (if provided) - if (featureDescription.trim()) { - const overall = await analyzeFeatureWithAI( - featureName, - featureDescription, - [], - projectType - ) - setAiAnalysis({ - suggested_name: featureName, - complexity: overall.complexity, - implementation_details: [], - technical_requirements: [], - estimated_effort: 'Medium', - dependencies: [], - api_endpoints: [], - database_tables: [], - confidence_score: 0.8, - }) - } + // Aggregate requirements texts for richer context + const reqTexts = requirements.map(r => r.text).filter(t => t && t.trim()) + const overall = await analyzeFeatureWithAI( + featureName, + featureDescription, + reqTexts, + projectType + ) - // 2) Analyze each requirement to get logic rules - const updated = [...requirements] - for (let i = 0; i < updated.length; i++) { - const req = updated[i] - if (!req.text.trim()) continue - const perReq = await analyzeFeatureWithAI(featureName, req.text, [], projectType) - const rules: string[] = Array.isArray(perReq.logicRules) ? perReq.logicRules : [] - updated[i] = { ...req, rules } - } - setRequirements(updated) + setAiAnalysis({ + suggested_name: featureName, + complexity: overall.complexity, + implementation_details: [], + technical_requirements: [], + estimated_effort: 'Medium', + dependencies: [], + api_endpoints: [], + database_tables: [], + confidence_score: 0.9, + }) + + // Capture dynamic logic rules (editable) + setLogicRules(Array.isArray(overall.logicRules) ? overall.logicRules : []) } catch (e: any) { setAnalysisError(e?.message || 'AI analysis failed') } finally { @@ -97,12 +90,14 @@ export function AICustomFeatureCreator({ return (
-
-
+
+

AI-Powered Feature Creator

+
+
@@ -198,20 +193,46 @@ export function AICustomFeatureCreator({
)} - {/* Footer */} -
- {aiAnalysis && ( -
- Overall Complexity: {aiAnalysis.complexity} + {aiAnalysis && ( +
+ +
+ {logicRules.map((rule, idx) => ( +
+
R{idx + 1}
+ { + const next = [...logicRules] + next[idx] = e.target.value + setLogicRules(next) + }} + className="bg-white/10 border-white/20 text-white placeholder:text-white/40" + /> + +
+ ))}
- )} - - -
+ +
+ )}
+
+
+ {aiAnalysis && ( +
+ Overall Complexity: {aiAnalysis.complexity} +
+ )} + + +
+
) diff --git a/src/components/main-dashboard.tsx b/src/components/main-dashboard.tsx index c231183..0dc14fe 100644 --- a/src/components/main-dashboard.tsx +++ b/src/components/main-dashboard.tsx @@ -6,6 +6,7 @@ import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" +import { Checkbox } from "@/components/ui/checkbox" import { ArrowRight, Plus, Globe, BarChart3, Zap, Code, Search, Star, Clock, Users, Layers, AlertCircle, Edit, Trash2 } from "lucide-react" import { useTemplates } from "@/hooks/useTemplates" import { CustomTemplateForm } from "@/components/custom-template-form" @@ -441,12 +442,13 @@ function FeatureSelectionStep({ template, onNext, onBack, -}: { template: Template; onNext: () => void; onBack: () => void }) { +}: { template: Template; onNext: (selected: TemplateFeature[]) => void; onBack: () => void }) { const { fetchFeatures, createFeature, updateFeature, deleteFeature } = useTemplates() const [features, setFeatures] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [newFeature, setNewFeature] = useState({ name: '', description: '', complexity: 'medium' as 'low' | 'medium' | 'high' }) + const [selectedIds, setSelectedIds] = useState>(new Set()) const [showAIModal, setShowAIModal] = useState(false) const load = async () => { @@ -466,7 +468,7 @@ function FeatureSelectionStep({ const handleAddCustom = async () => { if (!newFeature.name.trim()) return - await createFeature(template.id, { + const created = await createFeature(template.id, { name: newFeature.name, description: newFeature.description, feature_type: 'custom', @@ -475,6 +477,11 @@ function FeatureSelectionStep({ created_by_user: true, }) setNewFeature({ name: '', description: '', complexity: 'medium' }) + setSelectedIds((prev) => { + const next = new Set(prev) + if (created?.id) next.add(created.id) + return next + }) await load() } @@ -497,18 +504,39 @@ function FeatureSelectionStep({ const handleDelete = async (f: TemplateFeature) => { await deleteFeature(f.id, { isCustom: f.feature_type === 'custom' }) + setSelectedIds((prev) => { + const next = new Set(prev) + next.delete(f.id) + return next + }) await load() } + const toggleSelect = (f: TemplateFeature) => { + setSelectedIds((prev) => { + const next = new Set(prev) + if (next.has(f.id)) next.delete(f.id) + else next.add(f.id) + return next + }) + } + const section = (title: string, list: TemplateFeature[]) => (

{title} ({list.length})

{list.map((f) => ( - + - {f.name} +
+ toggleSelect(f)} + className="border-white/20 data-[state=checked]:bg-orange-500 data-[state=checked]:border-orange-500" + /> + {f.name} +
{f.feature_type === 'custom' && (
- +
+
Select at least 3 features to continue. Selected {selectedIds.size}/3.
+
+
+ ) +} + +// Business Questions Step Component +function BusinessQuestionsStep({ + template, + selected, + onBack, + onDone, +}: { template: Template; selected: TemplateFeature[]; onBack: () => void; onDone: (completeData: any, recommendations: any) => void }) { + const [businessQuestions, setBusinessQuestions] = useState([]) + const [businessAnswers, setBusinessAnswers] = useState>({}) + const [loading, setLoading] = useState(true) + const [submitting, setSubmitting] = useState(false) + const [error, setError] = useState(null) + + useEffect(() => { + const load = async () => { + try { + setLoading(true) + setError(null) + if (selected.length === 0) { + setError('No features selected') + return + } + const resp = await fetch('http://localhost:8001/api/v1/generate-comprehensive-business-questions', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + allFeatures: selected, + projectName: template.title, + projectType: template.type || template.category, + totalFeatures: selected.length, + }), + }) + if (!resp.ok) throw new Error(`HTTP ${resp.status}`) + const data = await resp.json() + const qs: string[] = data?.data?.businessQuestions || [] + setBusinessQuestions(qs) + const init: Record = {} + qs.forEach((_, i) => (init[i] = '')) + setBusinessAnswers(init) + } catch (e: any) { + setError(e?.message || 'Failed to load questions') + } finally { + setLoading(false) + } + } + load() + }, [template.id, selected.map(s => s.id).join(',')]) + + const answeredCount = Object.values(businessAnswers).filter((a) => a && a.trim()).length + + if (loading) { + return ( +
+
+
+

AI is generating comprehensive business questions...

+

Analyzing {selected.length} features as integrated system

+
+
+ ) + } + + if (error) { + return ( +
+
+
Error Loading Questions
+
{error}
+
+ + +
+
+
+ ) + } + + const handleSubmit = async () => { + try { + setSubmitting(true) + const answeredCount = Object.values(businessAnswers).filter((a) => a && a.trim()).length + if (answeredCount === 0) return + + const completeData = { + projectName: template.title, + projectType: template.type || template.category, + allFeatures: selected, + businessQuestions, + businessAnswers, + timestamp: new Date().toISOString(), + featureName: `${template.title} - Integrated System`, + description: `Complete ${template.type || template.category} system with ${selected.length} integrated features`, + requirements: (selected as any[]).flatMap((f: any) => f.requirements || []), + complexity: + (selected as any[]).some((f: any) => f.complexity === 'high') + ? 'high' + : (selected as any[]).some((f: any) => f.complexity === 'medium') + ? 'medium' + : 'low', + logicRules: (selected as any[]).flatMap((f: any) => f.logicRules || []), + } + + const resp = await fetch('http://localhost:8002/api/v1/select', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(completeData), + }) + if (!resp.ok) throw new Error(`HTTP ${resp.status}`) + const recommendations = await resp.json() + onDone(completeData, recommendations) + } catch (e) { + console.error('Tech stack selection failed', e) + } finally { + setSubmitting(false) + } + } + + return ( +
+
+

Business Context Questions

+

Help us refine recommendations by answering these questions.

+

Analyzing {selected.length} integrated features

+
+ +
+ {businessQuestions.map((q, i) => ( +
+ +