diff --git a/AUTH_IMPLEMENTATION.md b/AUTH_IMPLEMENTATION.md deleted file mode 100644 index 5bd1e0a..0000000 --- a/AUTH_IMPLEMENTATION.md +++ /dev/null @@ -1,231 +0,0 @@ -# Authentication Implementation - -## Overview - -This document describes the authentication system implementation with proper error handling, separate signup/signin routes, and email verification flow. - -## Architecture - -### Backend (User Auth Service) -- **Port**: 8011 -- **Base URL**: `http://localhost:8011` -- **Database**: PostgreSQL -- **Features**: JWT authentication, email verification, session management - -### Frontend (Next.js App) -- **Port**: 3001 -- **Base URL**: `http://localhost:3001` -- **Framework**: Next.js 15.4.6 with TypeScript -- **UI**: Tailwind CSS + shadcn/ui components - -## Routes Structure - -### Frontend Routes -``` -/signup - User registration page -/signin - User login page -/auth - Redirects to /signin (legacy support) -/verify-email - Email verification page (handled by backend redirect) -``` - -### Backend API Endpoints -``` -POST /api/auth/register - User registration -POST /api/auth/login - User login -GET /api/auth/verify-email - Email verification (redirects to frontend) -POST /api/auth/logout - User logout -POST /api/auth/refresh - Token refresh -GET /api/auth/me - Get user profile -``` - -## User Flow - -### 1. Registration Flow -1. User visits `/signup` -2. Fills out registration form -3. Submits form → `POST /api/auth/register` -4. Backend creates user account and sends verification email -5. Frontend shows success message and redirects to `/signin` after 3 seconds -6. User receives email with verification link - -### 2. Email Verification Flow -1. User clicks verification link in email -2. Link points to: `http://localhost:8011/api/auth/verify-email?token=` -3. Backend verifies token and redirects to: `http://localhost:3001/signin?verified=true` -4. Frontend displays success message: "Email verified successfully! You can now sign in to your account." - -### 3. Login Flow -1. User visits `/signin` -2. Fills out login form -3. Submits form → `POST /api/auth/login` -4. Backend validates credentials and returns JWT tokens -5. Frontend stores tokens and redirects to dashboard (`/`) - -## Error Handling - -### Backend Error Responses -All API endpoints return consistent error format: -```json -{ - "success": false, - "error": "Error type", - "message": "Detailed error message" -} -``` - -### Frontend Error Handling -- **Network Errors**: Display user-friendly messages -- **API Errors**: Show specific error messages from backend -- **Validation Errors**: Client-side validation with immediate feedback -- **Authentication Errors**: Clear messaging for login/registration issues - -### Common Error Scenarios - -#### Registration Errors -- **Email already exists**: "An account with this email already exists" -- **Username taken**: "Username is already taken" -- **Invalid email format**: "Please enter a valid email address" -- **Weak password**: "Password must be at least 8 characters long" -- **Missing fields**: "Please fill in all required fields" - -#### Login Errors -- **Invalid credentials**: "Invalid email or password" -- **Email not verified**: "Please verify your email before signing in" -- **Account locked**: "Account is temporarily locked due to multiple failed attempts" - -#### Email Verification Errors -- **Invalid token**: "Verification link is invalid or has expired" -- **Already verified**: "Email is already verified" -- **Token expired**: "Verification link has expired. Please request a new one" - -## Components Structure - -### Signup Flow -``` -/signup -├── SignUpPage (main container) -├── SignUpForm (form component) -└── Success State (after registration) -``` - -### Signin Flow -``` -/signin -├── SignInPage (main container) -├── SignInForm (form component) -└── Verification Messages (from URL params) -``` - -## API Integration - -### Authentication Handler (`authenticationHandler.tsx`) -- Handles API calls to backend -- Proper error propagation -- TypeScript interfaces for type safety - -### Key Functions -```typescript -registerUser(data: RegisterData): Promise -loginUser(email: string, password: string): Promise -``` - -## Security Features - -### Backend Security -- JWT token authentication -- Password hashing (bcrypt) -- Rate limiting on auth endpoints -- CORS configuration -- Helmet security headers -- Session management - -### Frontend Security -- Token storage in localStorage -- Automatic token refresh -- Secure API communication -- Input validation and sanitization - -## Environment Configuration - -### Backend (.env) -```env -PORT=8011 -FRONTEND_URL=http://localhost:3001 -POSTGRES_HOST=localhost -POSTGRES_DB=user_auth -JWT_SECRET=your-secret-key -SMTP_HOST=smtp.gmail.com -SMTP_PORT=587 -SMTP_USER=your-email@gmail.com -SMTP_PASS=your-app-password -``` - -### Frontend (.env.local) -```env -NEXT_PUBLIC_API_URL=http://localhost:8011 -NEXT_PUBLIC_FRONTEND_URL=http://localhost:3001 -``` - -## Testing the Implementation - -### 1. Start Services -```bash -# Backend -cd automated-dev-pipeline -docker-compose up user-auth - -# Frontend -cd codenuk-frontend-dark-theme -npm run dev -``` - -### 2. Test Registration -1. Visit `http://localhost:3001/signup` -2. Fill out registration form -3. Submit and check for success message -4. Check email for verification link - -### 3. Test Email Verification -1. Click verification link in email -2. Should redirect to `http://localhost:3001/signin?verified=true` -3. Verify success message appears - -### 4. Test Login -1. Visit `http://localhost:3001/signin` -2. Enter credentials -3. Should redirect to dashboard on success - -## Troubleshooting - -### Common Issues - -1. **CORS Errors** - - Check backend CORS configuration - - Verify frontend URL in allowed origins - -2. **Email Not Sending** - - Check SMTP configuration in backend - - Verify email credentials - -3. **Verification Link Not Working** - - Check frontend URL in backend configuration - - Verify token expiration settings - -4. **Login Fails After Verification** - - Check if user is properly verified in database - - Verify JWT token generation - -### Debug Steps -1. Check browser network tab for API calls -2. Check backend logs for errors -3. Verify database connections -4. Test API endpoints directly with Postman/curl - -## Future Enhancements - -1. **Password Reset Flow** -2. **Two-Factor Authentication** -3. **Social Login Integration** -4. **Account Lockout Protection** -5. **Session Management Dashboard** -6. **Audit Logging** diff --git a/DYNAMIC_TEMPLATES.md b/DYNAMIC_TEMPLATES.md deleted file mode 100644 index 63eb0bd..0000000 --- a/DYNAMIC_TEMPLATES.md +++ /dev/null @@ -1,118 +0,0 @@ -# Dynamic Templates Implementation - -## Overview -The frontend now fetches templates dynamically from the database instead of using static templates. This allows for real-time template management and custom template creation. - -## Changes Made - -### 1. Template Service (`src/lib/template-service.ts`) -- Created service to communicate with the template-manager API -- Handles fetching templates by category, individual templates, and creating new templates -- Includes TypeScript interfaces for type safety - -### 2. Custom Hook (`src/hooks/useTemplates.ts`) -- Manages template data fetching and state -- Converts database templates to UI format -- Handles loading states and error handling -- Provides template feature fetching - -### 3. Custom Template Form (`src/components/custom-template-form.tsx`) -- Form component for creating new templates -- Includes all required fields: type, title, description, category -- Optional fields: icon, gradient, border, text, subtext -- Validates required fields before submission - -### 4. Updated Main Dashboard (`src/components/main-dashboard.tsx`) -- Replaced static templates with dynamic database templates -- Added loading and error states -- Dynamic category generation based on available templates -- Custom template creation functionality -- Fallback to static templates if database is unavailable - -### 5. UI Components -- Added `textarea.tsx` component for form inputs -- Enhanced existing components with proper styling - -## API Integration - -### Template Manager Service -- **Base URL**: `http://localhost:8009` -- **Endpoints**: - - `GET /api/templates` - Get all templates grouped by category - - `GET /api/templates/:id` - Get specific template with features - - `GET /api/templates/type/:type` - Get template by type - - `POST /api/templates` - Create new template - -### Database Schema -Templates are stored in PostgreSQL with the following structure: -```sql -CREATE TABLE templates ( - id UUID PRIMARY KEY, - type VARCHAR(100) UNIQUE, - title VARCHAR(200), - description TEXT, - category VARCHAR(100), - icon VARCHAR(50), - gradient VARCHAR(100), - border VARCHAR(100), - text VARCHAR(100), - subtext VARCHAR(100), - is_active BOOLEAN, - created_at TIMESTAMP, - updated_at TIMESTAMP -); -``` - -## Usage - -### Viewing Templates -1. Templates are automatically loaded from the database on page load -2. If database is unavailable, fallback static templates are shown -3. Templates are grouped by category dynamically - -### Creating Custom Templates -1. Click "Create Custom Template" button -2. Fill in required fields (type, title, description, category) -3. Optionally add styling fields (icon, gradient, border, text, subtext) -4. Submit to create the template in the database -5. Template will appear in the list after creation - -### Template Features -- Each template can have associated features stored in `template_features` table -- Features are fetched when a template is selected -- Features include complexity, type, and usage statistics - -## Error Handling -- Network errors show retry button -- Loading states with spinner -- Graceful fallback to static templates -- Form validation for required fields - -## ✅ Implemented Features - -### Template Management -- ✅ **Dynamic Template Display** - Templates fetched from database -- ✅ **Custom Template Creation** - Create new templates via form -- ✅ **Template Editing** - Edit existing templates -- ✅ **Template Deletion** - Delete templates with confirmation -- ✅ **Real-time Updates** - Changes reflect immediately in UI - -### API Endpoints -- ✅ `GET /api/templates` - Get all templates grouped by category -- ✅ `GET /api/templates/:id` - Get specific template with features -- ✅ `POST /api/templates` - Create new template -- ✅ `PUT /api/templates/:id` - Update existing template -- ✅ `DELETE /api/templates/:id` - Delete template (soft delete) - -### Database Operations -- ✅ **Soft Delete** - Templates are marked as inactive rather than physically deleted -- ✅ **Data Integrity** - All operations maintain referential integrity -- ✅ **Error Handling** - Comprehensive error handling for all operations - -## Future Enhancements -- Feature management for templates -- Bulk template operations -- Template versioning -- Template sharing between users -- Template import/export functionality -- Template analytics and usage tracking diff --git a/package-lock.json b/package-lock.json index 72a0464..321aba5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,8 +34,9 @@ "clsx": "^2.1.1", "date-fns": "^4.1.0", "embla-carousel-react": "^8.6.0", + "framer-motion": "^12.23.22", "lucide-react": "^0.539.0", - "next": "15.4.6", + "next": "^15.5.4", "react": "19.1.0", "react-day-picker": "^9.9.0", "react-dom": "19.1.0", @@ -932,9 +933,9 @@ } }, "node_modules/@next/env": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.4.6.tgz", - "integrity": "sha512-yHDKVTcHrZy/8TWhj0B23ylKv5ypocuCwey9ZqPyv4rPdUdRzpGCkSi03t04KBPyU96kxVtUqx6O3nE1kpxASQ==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.4.tgz", + "integrity": "sha512-27SQhYp5QryzIT5uO8hq99C69eLQ7qkzkDPsk3N+GuS2XgOgoYEeOav7Pf8Tn4drECOVDsDg8oj+/DVy8qQL2A==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -948,9 +949,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.4.6.tgz", - "integrity": "sha512-667R0RTP4DwxzmrqTs4Lr5dcEda9OxuZsVFsjVtxVMVhzSpo6nLclXejJVfQo2/g7/Z9qF3ETDmN3h65mTjpTQ==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.4.tgz", + "integrity": "sha512-nopqz+Ov6uvorej8ndRX6HlxCYWCO3AHLfKK2TYvxoSB2scETOcfm/HSS3piPqc3A+MUgyHoqE6je4wnkjfrOA==", "cpu": [ "arm64" ], @@ -964,9 +965,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.4.6.tgz", - "integrity": "sha512-KMSFoistFkaiQYVQQnaU9MPWtp/3m0kn2Xed1Ces5ll+ag1+rlac20sxG+MqhH2qYWX1O2GFOATQXEyxKiIscg==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.4.tgz", + "integrity": "sha512-QOTCFq8b09ghfjRJKfb68kU9k2K+2wsC4A67psOiMn849K9ZXgCSRQr0oVHfmKnoqCbEmQWG1f2h1T2vtJJ9mA==", "cpu": [ "x64" ], @@ -980,9 +981,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.4.6.tgz", - "integrity": "sha512-PnOx1YdO0W7m/HWFeYd2A6JtBO8O8Eb9h6nfJia2Dw1sRHoHpNf6lN1U4GKFRzRDBi9Nq2GrHk9PF3Vmwf7XVw==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.4.tgz", + "integrity": "sha512-eRD5zkts6jS3VfE/J0Kt1VxdFqTnMc3QgO5lFE5GKN3KDI/uUpSyK3CjQHmfEkYR4wCOl0R0XrsjpxfWEA++XA==", "cpu": [ "arm64" ], @@ -996,9 +997,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.4.6.tgz", - "integrity": "sha512-XBbuQddtY1p5FGPc2naMO0kqs4YYtLYK/8aPausI5lyOjr4J77KTG9mtlU4P3NwkLI1+OjsPzKVvSJdMs3cFaw==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.4.tgz", + "integrity": "sha512-TOK7iTxmXFc45UrtKqWdZ1shfxuL4tnVAOuuJK4S88rX3oyVV4ZkLjtMT85wQkfBrOOvU55aLty+MV8xmcJR8A==", "cpu": [ "arm64" ], @@ -1012,9 +1013,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.4.6.tgz", - "integrity": "sha512-+WTeK7Qdw82ez3U9JgD+igBAP75gqZ1vbK6R8PlEEuY0OIe5FuYXA4aTjL811kWPf7hNeslD4hHK2WoM9W0IgA==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.4.tgz", + "integrity": "sha512-7HKolaj+481FSW/5lL0BcTkA4Ueam9SPYWyN/ib/WGAFZf0DGAN8frNpNZYFHtM4ZstrHZS3LY3vrwlIQfsiMA==", "cpu": [ "x64" ], @@ -1028,9 +1029,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.4.6.tgz", - "integrity": "sha512-XP824mCbgQsK20jlXKrUpZoh/iO3vUWhMpxCz8oYeagoiZ4V0TQiKy0ASji1KK6IAe3DYGfj5RfKP6+L2020OQ==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.4.tgz", + "integrity": "sha512-nlQQ6nfgN0nCO/KuyEUwwOdwQIGjOs4WNMjEUtpIQJPR2NUfmGpW2wkJln1d4nJ7oUzd1g4GivH5GoEPBgfsdw==", "cpu": [ "x64" ], @@ -1044,9 +1045,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.4.6.tgz", - "integrity": "sha512-FxrsenhUz0LbgRkNWx6FRRJIPe/MI1JRA4W4EPd5leXO00AZ6YU8v5vfx4MDXTvN77lM/EqsE3+6d2CIeF5NYg==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.4.tgz", + "integrity": "sha512-PcR2bN7FlM32XM6eumklmyWLLbu2vs+D7nJX8OAIoWy69Kef8mfiN4e8TUv2KohprwifdpFKPzIP1njuCjD0YA==", "cpu": [ "arm64" ], @@ -1060,9 +1061,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.4.6.tgz", - "integrity": "sha512-T4ufqnZ4u88ZheczkBTtOF+eKaM14V8kbjud/XrAakoM5DKQWjW09vD6B9fsdsWS2T7D5EY31hRHdta7QKWOng==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.4.tgz", + "integrity": "sha512-1ur2tSHZj8Px/KMAthmuI9FMp/YFusMMGoRNJaRZMOlSkgvLjzosSdQI0cJAKogdHl3qXUQKL9MGaYvKwA7DXg==", "cpu": [ "x64" ], @@ -4605,9 +4606,9 @@ } }, "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -6124,6 +6125,33 @@ "integrity": "sha512-0tLU0FOedVY7lrvN4LK0DVj6FTuYM0pWDpN97/8UTZE2lx1+OwX8+2uL7IOWc2PmktYTHQjMT6FvZZ3SGCdZdg==", "license": "CC0-1.0" }, + "node_modules/framer-motion": { + "version": "12.23.22", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.22.tgz", + "integrity": "sha512-ZgGvdxXCw55ZYvhoZChTlG6pUuehecgvEAJz0BHoC5pQKW1EC5xf1Mul1ej5+ai+pVY0pylyFfdl45qnM1/GsA==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.23.21", + "motion-utils": "^12.23.6", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -7542,6 +7570,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/motion-dom": { + "version": "12.23.21", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.21.tgz", + "integrity": "sha512-5xDXx/AbhrfgsQmSE7YESMn4Dpo6x5/DTZ4Iyy4xqDvVHWvFVoV+V2Ri2S/ksx+D40wrZ7gPYiMWshkdoqNgNQ==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.23.6" + } + }, + "node_modules/motion-utils": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -7590,12 +7633,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/next/-/next-15.4.6.tgz", - "integrity": "sha512-us++E/Q80/8+UekzB3SAGs71AlLDsadpFMXVNM/uQ0BMwsh9m3mr0UNQIfjKed8vpWXsASe+Qifrnu1oLIcKEQ==", + "version": "15.5.4", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.4.tgz", + "integrity": "sha512-xH4Yjhb82sFYQfY3vbkJfgSDgXvBB6a8xPs9i35k6oZJRoQRihZH+4s9Yo2qsWpzBmZ3lPXaJ2KPXLfkvW4LnA==", "license": "MIT", "dependencies": { - "@next/env": "15.4.6", + "@next/env": "15.5.4", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -7608,14 +7651,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.4.6", - "@next/swc-darwin-x64": "15.4.6", - "@next/swc-linux-arm64-gnu": "15.4.6", - "@next/swc-linux-arm64-musl": "15.4.6", - "@next/swc-linux-x64-gnu": "15.4.6", - "@next/swc-linux-x64-musl": "15.4.6", - "@next/swc-win32-arm64-msvc": "15.4.6", - "@next/swc-win32-x64-msvc": "15.4.6", + "@next/swc-darwin-arm64": "15.5.4", + "@next/swc-darwin-x64": "15.5.4", + "@next/swc-linux-arm64-gnu": "15.5.4", + "@next/swc-linux-arm64-musl": "15.5.4", + "@next/swc-linux-x64-gnu": "15.5.4", + "@next/swc-linux-x64-musl": "15.5.4", + "@next/swc-win32-arm64-msvc": "15.5.4", + "@next/swc-win32-x64-msvc": "15.5.4", "sharp": "^0.34.3" }, "peerDependencies": { diff --git a/package.json b/package.json index 9eb06de..237f97e 100644 --- a/package.json +++ b/package.json @@ -35,8 +35,9 @@ "clsx": "^2.1.1", "date-fns": "^4.1.0", "embla-carousel-react": "^8.6.0", + "framer-motion": "^12.23.22", "lucide-react": "^0.539.0", - "next": "15.4.6", + "next": "^15.5.4", "react": "19.1.0", "react-day-picker": "^9.9.0", "react-dom": "19.1.0", diff --git a/src/app/api/ai/tech-recommendations/route.ts b/src/app/api/ai/tech-recommendations/route.ts new file mode 100644 index 0000000..283d4da --- /dev/null +++ b/src/app/api/ai/tech-recommendations/route.ts @@ -0,0 +1,116 @@ +import { NextRequest, NextResponse } from 'next/server'; + +export async function POST(request: NextRequest) { + try { + console.log('🚀 Tech recommendations API called - redirecting to unified service'); + + // Parse request body + const body = await request.json(); + console.log('📊 Request body:', { + template: body.template?.title, + featuresCount: body.features?.length, + businessQuestionsCount: body.businessContext?.questions?.length, + }); + + // Validate required fields + if (!body.template || !body.features || !body.businessContext) { + return NextResponse.json( + { + success: false, + error: 'Missing required fields: template, features, or businessContext', + }, + { status: 400 } + ); + } + + // Validate template structure + if (!body.template.title || !body.template.category) { + return NextResponse.json( + { + success: false, + error: 'Template must have title and category', + }, + { status: 400 } + ); + } + + // Validate features array + if (!Array.isArray(body.features) || body.features.length === 0) { + return NextResponse.json( + { + success: false, + error: 'Features must be a non-empty array', + }, + { status: 400 } + ); + } + + // Validate business context + if (!body.businessContext.questions || !Array.isArray(body.businessContext.questions)) { + return NextResponse.json( + { + success: false, + error: 'Business context must have questions array', + }, + { status: 400 } + ); + } + + // Redirect to unified service through API Gateway + const apiGatewayUrl = process.env.BACKEND_URL || 'https://backend.codenuk.com'; + + const response = await fetch(`${apiGatewayUrl}/api/unified/comprehensive-recommendations`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + ...body, + // Add optional parameters for template-based and domain-based recommendations + templateId: body.template.id, + budget: 15000, // Default budget - could be made configurable + domain: body.template.category?.toLowerCase() || 'general', + includeClaude: true, + includeTemplateBased: true, + includeDomainBased: true, + }), + }); + + if (!response.ok) { + throw new Error(`Unified service error: ${response.status}`); + } + + const recommendations = await response.json(); + console.log('✅ Comprehensive recommendations received from unified service:', { + success: recommendations.success, + hasClaudeRecommendations: !!recommendations.data?.claude?.success, + hasTemplateRecommendations: !!recommendations.data?.templateBased?.success, + hasDomainRecommendations: !!recommendations.data?.domainBased?.success, + }); + + // Return the recommendations + return NextResponse.json(recommendations); + } catch (error) { + console.error('❌ Error in tech recommendations API:', error); + + return NextResponse.json( + { + success: false, + error: error instanceof Error ? error.message : 'Internal server error', + }, + { status: 500 } + ); + } +} + +// Handle OPTIONS request for CORS +export async function OPTIONS(request: NextRequest) { + return new NextResponse(null, { + status: 200, + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'POST, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type, Authorization', + }, + }); +} diff --git a/src/components/apis/authApiClients.tsx b/src/components/apis/authApiClients.tsx index 15eba34..3d12a7b 100644 --- a/src/components/apis/authApiClients.tsx +++ b/src/components/apis/authApiClients.tsx @@ -87,6 +87,7 @@ export const logout = async () => { export const authApiClient = axios.create({ baseURL: BACKEND_URL, withCredentials: true, + timeout: 10000, // 10 second timeout }); // Add auth token to requests @@ -127,8 +128,34 @@ const addTokenRefreshInterceptor = (client: typeof authApiClient) => { try { const status = error?.response?.status const data = error?.response?.data - console.error('🛑 API error:', { url: error?.config?.url, method: error?.config?.method, status, data }) - } catch (_) {} + const url = error?.config?.url + const method = error?.config?.method + const message = error?.message || 'Unknown error' + const code = error?.code + + // Check if it's a network connectivity issue + if (code === 'ECONNREFUSED' || code === 'ENOTFOUND' || code === 'ETIMEDOUT') { + console.error('🛑 Network connectivity issue:', { + url: url || 'Unknown URL', + method: method || 'Unknown method', + code: code, + message: message + }) + } else { + console.error('🛑 API error:', { + url: url || 'Unknown URL', + method: method || 'Unknown method', + status: status || 'No status', + data: data || 'No response data', + message: message, + errorType: error?.name || 'Unknown error type', + code: code + }) + } + } catch (debugError) { + console.error('🛑 Error logging failed:', debugError) + console.error('🛑 Original error:', error) + } const originalRequest = error.config; const isRefreshEndpoint = originalRequest?.url?.includes('/api/auth/refresh'); diff --git a/src/components/business-context/typeform-survey.tsx b/src/components/business-context/typeform-survey.tsx new file mode 100644 index 0000000..afbd2a0 --- /dev/null +++ b/src/components/business-context/typeform-survey.tsx @@ -0,0 +1,332 @@ +'use client' + +import { useState, useEffect, useCallback } from 'react' +import { motion, AnimatePresence } from 'framer-motion' +import { ChevronLeft, ChevronRight, ArrowRight } from 'lucide-react' + +interface Question { + id: number + question: string + answer: string +} + +interface TypeformSurveyProps { + questions: string[] + onComplete: (answers: Question[]) => void + onProgress?: (answers: Question[]) => void + onBack: () => void + projectName?: string +} + +export default function TypeformSurvey({ + questions, + onComplete, + onProgress, + onBack, + projectName = 'your project' +}: TypeformSurveyProps) { + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0) + const [answers, setAnswers] = useState>({}) + const [isTransitioning, setIsTransitioning] = useState(false) + + const totalQuestions = questions.length + const progress = ((currentQuestionIndex + 1) / totalQuestions) * 100 + const answeredCount = Object.values(answers).filter(answer => answer.trim()).length + + // Initialize answers + useEffect(() => { + const initialAnswers: Record = {} + questions.forEach((_, index) => { + initialAnswers[index] = '' + }) + setAnswers(initialAnswers) + }, [questions]) + + const handleAnswerChange = (value: string) => { + const newAnswers = { + ...answers, + [currentQuestionIndex]: value + } + setAnswers(newAnswers) + + // Call onProgress callback if provided + if (onProgress) { + const questionAnswers: Question[] = questions.map((question, index) => ({ + id: index, + question, + answer: newAnswers[index] || '' + })) + onProgress(questionAnswers) + } + } + + const goToNext = useCallback(() => { + if (currentQuestionIndex < totalQuestions - 1) { + setIsTransitioning(true) + setTimeout(() => { + setCurrentQuestionIndex(prev => prev + 1) + setIsTransitioning(false) + }, 150) + } else { + // Submit all answers + const questionAnswers: Question[] = questions.map((question, index) => ({ + id: index, + question, + answer: answers[index] || '' + })) + onComplete(questionAnswers) + } + }, [currentQuestionIndex, totalQuestions, answers, questions, onComplete]) + + const goToPrevious = useCallback(() => { + if (currentQuestionIndex > 0) { + setIsTransitioning(true) + setTimeout(() => { + setCurrentQuestionIndex(prev => prev - 1) + setIsTransitioning(false) + }, 150) + } else { + onBack() + } + }, [currentQuestionIndex, onBack]) + + const handleKeyPress = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault() + goToNext() + } + } + + const currentAnswer = answers[currentQuestionIndex] || '' + const canProceed = currentAnswer.trim().length > 0 + + return ( +
+ {/* Progress Bar */} +
+
+
+
+ Question {currentQuestionIndex + 1} of {totalQuestions} +
+
+ {Math.round(progress)}% complete +
+
+
+ +
+
+
+ + {/* Main Content */} +
+
+ + + {/* Question */} +
+ + {questions[currentQuestionIndex]} + + + + Help us understand your {projectName} better + +
+ + {/* Answer Input */} + +