frontend changes

This commit is contained in:
Chandini 2025-09-17 10:16:20 +05:30
parent 81ad734f47
commit 8b5c53ef4c
6 changed files with 26 additions and 63 deletions

View File

@ -3,7 +3,7 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev --turbopack -p 3001", "dev": "next dev -p 3001",
"build": "next build", "build": "next build",
"start": "next start -p 3001", "start": "next start -p 3001",
"lint": "next lint" "lint": "next lint"

View File

@ -1,18 +1,11 @@
import type React from "react" import type React from "react"
import type { Metadata } from "next" import type { Metadata } from "next"
import { Poppins } from "next/font/google"
import { AuthProvider } from "@/contexts/auth-context" import { AuthProvider } from "@/contexts/auth-context"
import { AppLayout } from "@/components/layout/app-layout" import { AppLayout } from "@/components/layout/app-layout"
import { ToastProvider } from "@/components/ui/toast" import { ToastProvider } from "@/components/ui/toast"
import "./globals.css" import "./globals.css"
import "@tldraw/tldraw/tldraw.css" import "@tldraw/tldraw/tldraw.css"
const poppins = Poppins({
subsets: ["latin"],
weight: ["300", "400", "500", "600", "700"],
variable: "--font-poppins",
})
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Codenuk - AI-Powered Project Builder", title: "Codenuk - AI-Powered Project Builder",
description: "Build scalable applications with AI-generated architecture and code", description: "Build scalable applications with AI-generated architecture and code",
@ -27,10 +20,16 @@ export default function RootLayout({
return ( return (
<html lang="en"> <html lang="en">
<head> <head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
<link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
/>
<style>{` <style>{`
html { html {
font-family: ${poppins.style.fontFamily}; font-family: 'Poppins', sans-serif;
--font-sans: ${poppins.variable}; --font-sans: 'Poppins', sans-serif;
} }
`}</style> `}</style>
</head> </head>

View File

@ -884,6 +884,8 @@ function FeatureSelectionStep({
}: { template: Template; onNext: (selected: TemplateFeature[]) => void; onBack: () => void }) { }: { template: Template; onNext: (selected: TemplateFeature[]) => void; onBack: () => void }) {
const { fetchFeatures, createFeature, updateFeature, deleteFeature } = useTemplates() const { fetchFeatures, createFeature, updateFeature, deleteFeature } = useTemplates()
const [features, setFeatures] = useState<TemplateFeature[]>([]) const [features, setFeatures] = useState<TemplateFeature[]>([])
const [essentialFeatures, setEssentialFeatures] = useState<TemplateFeature[]>([])
const [customFeatures, setCustomFeatures] = useState<TemplateFeature[]>([])
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set()) const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set())
@ -917,6 +919,9 @@ function FeatureSelectionStep({
}) })
console.log('[FeatureSelectionStep] All features with types:', data.map(f => ({ name: f.name, type: f.feature_type }))) console.log('[FeatureSelectionStep] All features with types:', data.map(f => ({ name: f.name, type: f.feature_type })))
setFeatures(data) setFeatures(data)
// Separate custom features from essential features
setEssentialFeatures(data.filter(f => f.feature_type !== 'custom'))
setCustomFeatures(data.filter(f => f.feature_type === 'custom'))
} catch (e) { } catch (e) {
console.error('[FeatureSelectionStep] Error loading features:', e) console.error('[FeatureSelectionStep] Error loading features:', e)
setError(e instanceof Error ? e.message : 'Failed to load features') setError(e instanceof Error ? e.message : 'Failed to load features')
@ -1085,10 +1090,10 @@ function FeatureSelectionStep({
<div className="max-w-7xl mx-auto space-y-8"> <div className="max-w-7xl mx-auto space-y-8">
<div className="text-center space-y-4"> <div className="text-center space-y-4">
<h1 className="text-4xl font-bold text-white">Select Features for {template.title}</h1> <h1 className="text-4xl font-bold text-white">Select Features for {template.title}</h1>
<p className="text-xl text-white/60 max-w-3xl mx-auto">Choose defaults or add your own custom features.</p> <p className="text-xl text-white/60 max-w-3xl mx-auto">Choose from essential and suggested features.</p>
</div> </div>
{features.length > 0 && section('Template Features', features)} {essentialFeatures.length > 0 && section('Essential Features', essentialFeatures)}
{/* Add custom feature with AI */} {/* Add custom feature with AI */}
<div className="bg-white/5 border border-white/10 rounded-xl p-6 space-y-4"> <div className="bg-white/5 border border-white/10 rounded-xl p-6 space-y-4">
@ -1110,7 +1115,7 @@ function FeatureSelectionStep({
</div> </div>
</div> </div>
{section('Your Custom Features', custom)} {customFeatures.length > 0 && section('Custom Features', customFeatures)}
{(showAIModal || editingFeature) && ( {(showAIModal || editingFeature) && (
<AICustomFeatureCreator <AICustomFeatureCreator
@ -1138,7 +1143,7 @@ function FeatureSelectionStep({
<div className="space-x-4"> <div className="space-x-4">
<Button variant="outline" onClick={onBack} className="border-white/20 text-white hover:bg-white/10 cursor-pointer">Back</Button> <Button variant="outline" onClick={onBack} className="border-white/20 text-white hover:bg-white/10 cursor-pointer">Back</Button>
<Button <Button
onClick={() => onNext(features.filter(f => selectedIds.has(f.id)))} onClick={() => onNext([...essentialFeatures, ...customFeatures].filter(f => selectedIds.has(f.id)))}
disabled={selectedIds.size < 3} disabled={selectedIds.size < 3}
className={`bg-orange-500 hover:bg-orange-400 text-black cursor-pointer font-semibold py-2 rounded-lg shadow ${selectedIds.size < 3 ? 'opacity-50 cursor-not-allowed' : ''}`} className={`bg-orange-500 hover:bg-orange-400 text-black cursor-pointer font-semibold py-2 rounded-lg shadow ${selectedIds.size < 3 ? 'opacity-50 cursor-not-allowed' : ''}`}
> >

View File

@ -4,8 +4,8 @@
*/ */
// Main backend URL - change this to update all API calls // Main backend URL - change this to update all API calls
export const BACKEND_URL = 'http://192.168.1.20:8000'; // export const BACKEND_URL = 'http://192.168.1.31:8000';
// export const BACKEND_URL = 'https://backend.codenuk.com'; export const BACKEND_URL = 'https://backend.codenuk.com';
// Realtime notifications socket URL (Template Manager emits notifications) // Realtime notifications socket URL (Template Manager emits notifications)

View File

@ -384,56 +384,15 @@ class TemplateService {
// Features API // Features API
async getFeaturesForTemplate(templateId: string): Promise<TemplateFeature[]> { async getFeaturesForTemplate(templateId: string): Promise<TemplateFeature[]> {
// Use merged endpoint to include custom features (returns custom_feature.id for custom items)
const dedupe = (items: TemplateFeature[]) => {
const byKey = new Map<string, TemplateFeature>()
const toKey = (f: TemplateFeature) => {
const normName = (f.name || '').trim().toLowerCase()
// Use normalized name for all features to enable proper deduplication
// between template_features and custom_features tables
return normName
}
const prefer = (a: TemplateFeature, b: TemplateFeature): TemplateFeature => {
// Prefer user-created, then higher usage_count, then newer updated_at
const aUser = !!a.created_by_user
const bUser = !!b.created_by_user
if (aUser !== bUser) return aUser ? a : b
const aUsage = typeof a.usage_count === 'number' ? a.usage_count : -1
const bUsage = typeof b.usage_count === 'number' ? b.usage_count : -1
if (aUsage !== bUsage) return aUsage > bUsage ? a : b
const aTime = a.updated_at ? Date.parse(a.updated_at) : 0
const bTime = b.updated_at ? Date.parse(b.updated_at) : 0
return aTime >= bTime ? a : b
}
for (const item of items) {
const key = toKey(item)
const existing = byKey.get(key)
if (!existing) {
byKey.set(key, item)
} else {
byKey.set(key, prefer(existing, item))
}
}
console.log('[getFeaturesForTemplate] Deduplication results:', {
originalCount: items.length,
deduplicatedCount: byKey.size,
duplicatesRemoved: items.length - byKey.size
})
return Array.from(byKey.values())
}
try { try {
const merged = await this.makeRequest<TemplateFeature[]>(`/api/templates/${templateId}/features`) const merged = await this.makeRequest<TemplateFeature[]>(`/api/templates/${templateId}/features`)
return dedupe(merged) console.log('[getFeaturesForTemplate] Raw merged features (no deduplication):', merged)
return merged
} catch { } catch {
// Fallback to compatible route in features router // Fallback to compatible route in features router
const defaults = await this.makeRequest<TemplateFeature[]>(`/api/features/templates/${templateId}/features`) const defaults = await this.makeRequest<TemplateFeature[]>(`/api/features/templates/${templateId}/features`)
return dedupe(defaults) console.log('[getFeaturesForTemplate] Raw fallback features (no deduplication):', defaults)
return defaults
} }
} }