feat: Apple-style donation-focused landing page + Azure OpenAI fixes
Major updates: - Replace homepage with clean, minimalist Apple-style landing page - Focus on donation messaging and mission statement - Add comprehensive AI chat analysis documentation - Fix Azure OpenAI configuration with correct endpoints - Update embedding API to use text-embedding-ada-002 (1536 dims) Landing Page Features: - Hero section with tagline "Every Scripture. Every Language. Forever Free" - Mission statement emphasizing free access - Matthew 10:8 verse highlight - 6 feature cards (Global Library, Multilingual, Prayer Wall, AI Chat, Privacy, Offline) - Donation CTA sections with PayPal and card options - "Why It Matters" section with dark background - Clean footer with navigation links Technical Changes: - Updated .env.local with new Azure credentials - Fixed vector-search.ts to support separate embed API version - Integrated AuthModal into Bible reader and prayers page - Made prayer filters collapsible and mobile-responsive - Changed language picker to single-select Documentation Created: - AI_CHAT_FIX_PLAN.md - Comprehensive implementation plan - AI_CHAT_VERIFICATION_FINDINGS.md - Database analysis - AI_CHAT_ANALYSIS_SUMMARY.md - Executive summary - AI_CHAT_STATUS_UPDATE.md - Current status and next steps - logo.svg - App logo (MenuBook icon) Build: ✅ Successful (Next.js 15.5.3) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
29
.duckversions/-20251010181141.618.env
Normal file
29
.duckversions/-20251010181141.618.env
Normal file
@@ -0,0 +1,29 @@
|
||||
# Database
|
||||
DATABASE_URL=postgresql://postgres:a3ppq@10.0.0.207:5432/biblical-guide
|
||||
DB_PASSWORD=a3ppq
|
||||
|
||||
# Build optimizations
|
||||
NEXT_TELEMETRY_DISABLED=1
|
||||
DISABLE_ESLINT_PLUGIN=true
|
||||
|
||||
# Authentication
|
||||
NEXTAUTH_URL=https://biblical-guide.com
|
||||
NEXTAUTH_SECRET=development-secret-change-in-production
|
||||
JWT_SECRET=development-jwt-secret-change-in-production
|
||||
|
||||
# Azure OpenAI
|
||||
AZURE_OPENAI_KEY=4sVcRlDOB7WnRK8oE6ATnokpKmc02JgY4GH2ng9y1vr1CyFT7ORLJQQJ99BDAC5RqLJXJ3w3AAAAACOGW8Kh
|
||||
AZURE_OPENAI_ENDPOINT=https://footprints-open-ai.openai.azure.com
|
||||
AZURE_OPENAI_DEPLOYMENT=gpt-4o
|
||||
AZURE_OPENAI_API_VERSION=2025-01-01-preview
|
||||
|
||||
# API Bible
|
||||
API_BIBLE_KEY=7b42606f8f809e155c9b0742c4f1849b
|
||||
|
||||
# Ollama for embeddings
|
||||
OLLAMA_API_URL=http://localhost:11434
|
||||
OLLAMA_EMBED_MODEL=llama3.1:latest
|
||||
BIBLE_JSON_DIR=/root/biblical-guide/bibles/json
|
||||
|
||||
# WebSocket port
|
||||
WEBSOCKET_PORT=3015
|
||||
13
.env.local
13
.env.local
@@ -12,13 +12,14 @@ NEXTAUTH_URL=https://biblical-guide.com
|
||||
NEXTAUTH_SECRET=development-secret-change-in-production
|
||||
JWT_SECRET=development-jwt-secret-change-in-production
|
||||
|
||||
# Azure OpenAI
|
||||
AZURE_OPENAI_KEY=4DhkkXVdDOXZ7xX1eOLHTHQQnbCy0jFYdA6RPJtyAdOMtO16nZmFJQQJ99BCACYeBjFXJ3w3AAABACOGHgNC
|
||||
AZURE_OPENAI_ENDPOINT=https://azureopenaiinstant.openai.azure.com
|
||||
# Azure OpenAI (Updated 2025-10-10)
|
||||
AZURE_OPENAI_KEY=42702a67a41547919877a2ab8e4837f9
|
||||
AZURE_OPENAI_ENDPOINT=https://footprints-ai.openai.azure.com
|
||||
AZURE_OPENAI_DEPLOYMENT=gpt-4o
|
||||
AZURE_OPENAI_API_VERSION=2024-05-01-preview
|
||||
AZURE_OPENAI_EMBED_DEPLOYMENT=embed-3
|
||||
EMBED_DIMS=3072
|
||||
AZURE_OPENAI_API_VERSION=2025-01-01-preview
|
||||
AZURE_OPENAI_EMBED_DEPLOYMENT=Text-Embedding-ada-002-V2
|
||||
AZURE_OPENAI_EMBED_API_VERSION=2023-05-15
|
||||
EMBED_DIMS=1536
|
||||
BIBLE_MD_PATH=./bibles/Biblia-Fidela-limba-romana.md
|
||||
LANG_CODE=ro
|
||||
TRANSLATION_CODE=FIDELA
|
||||
|
||||
402
AI_CHAT_ANALYSIS_SUMMARY.md
Normal file
402
AI_CHAT_ANALYSIS_SUMMARY.md
Normal file
@@ -0,0 +1,402 @@
|
||||
# AI Chat System Analysis - Executive Summary
|
||||
|
||||
**Date:** 2025-10-10
|
||||
**Analyst:** Claude Code
|
||||
**Status:** 🔴 Critical Issues Found - Requires User Action
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Bottom Line
|
||||
|
||||
The AI chat system has **excellent infrastructure** (vector database, search algorithms) but is blocked by **two critical issues**:
|
||||
|
||||
1. **❌ Azure OpenAI Not Configured** - No deployments exist or are accessible
|
||||
2. **❌ Wrong Bible Versions** - Priority languages (Romanian, Spanish, Italian) are NOT in database
|
||||
|
||||
**Good News:**
|
||||
- ✅ Ollama embedding model is being installed now (alternative to Azure)
|
||||
- ✅ Vector search code is production-ready
|
||||
- ✅ Database has 116 fully-embedded Bible versions
|
||||
|
||||
---
|
||||
|
||||
## 📊 System Status Report
|
||||
|
||||
### Vector Database: ✅ EXCELLENT (100%)
|
||||
|
||||
| Component | Status | Details |
|
||||
|-----------|--------|---------|
|
||||
| PostgreSQL Connection | ✅ Working | v17.5 |
|
||||
| pgvector Extension | ✅ Installed | v0.8.0 |
|
||||
| Schema `ai_bible` | ✅ Exists | Ready |
|
||||
| Total Vector Tables | ✅ 116 tables | 100% embedded |
|
||||
| Languages Supported | ⚠️ 47 languages | BUT missing priority ones |
|
||||
|
||||
### AI API Status: ❌ BLOCKED
|
||||
|
||||
| Service | Status | Issue |
|
||||
|---------|--------|-------|
|
||||
| Azure OpenAI Chat | ❌ Not Working | Deployment `gpt-4o` not found (404) |
|
||||
| Azure OpenAI Embeddings | ❌ Not Working | Deployment `embed-3` not found (404) |
|
||||
| Ollama (Local AI) | 🔄 Installing | `nomic-embed-text` downloading now |
|
||||
|
||||
### Vector Search Code: ✅ READY
|
||||
|
||||
| Feature | Status | Location |
|
||||
|---------|--------|----------|
|
||||
| Multi-table search | ✅ Implemented | `/lib/vector-search.ts:109` |
|
||||
| Hybrid search (vector + text) | ✅ Implemented | `/lib/vector-search.ts:163` |
|
||||
| Language filtering | ✅ Implemented | Table pattern: `bv_{lang}_{version}` |
|
||||
| Chat integration | ✅ Implemented | `/app/api/chat/route.ts:190` |
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Critical Issue #1: Wrong Bible Versions
|
||||
|
||||
### User Requirements vs. Reality
|
||||
|
||||
**What You Need:**
|
||||
- ✅ English
|
||||
- ❌ Romanian (ro)
|
||||
- ❌ Spanish (es)
|
||||
- ❌ Italian (it)
|
||||
|
||||
**What's in Database:**
|
||||
- ✅ English: 9 versions (KJV, ASV, etc.)
|
||||
- ❌ Romanian: **NOT FOUND**
|
||||
- ❌ Spanish: **NOT FOUND**
|
||||
- ❌ Italian: **NOT FOUND**
|
||||
|
||||
### What IS in the Database (47 Languages)
|
||||
|
||||
The 116 tables contain mostly obscure languages:
|
||||
- `ab` (Abkhazian), `ac` (Acholi), `ad` (Adangme), `ag` (Aguacateca), etc.
|
||||
- German (de), Dutch (nl), French (fr) ✓
|
||||
- But **NO Romanian, Spanish, or Italian**
|
||||
|
||||
### Where These Tables Came From
|
||||
|
||||
Looking at your environment variable:
|
||||
```bash
|
||||
BIBLE_MD_PATH=./bibles/Biblia-Fidela-limba-romana.md
|
||||
LANG_CODE=ro
|
||||
TRANSLATION_CODE=FIDELA
|
||||
```
|
||||
|
||||
You have Romanian Bible data (`Fidela`) but it's **NOT in the vector database yet**.
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Critical Issue #2: Azure OpenAI Not Configured
|
||||
|
||||
### The Problem
|
||||
|
||||
Your `.env.local` has:
|
||||
```bash
|
||||
AZURE_OPENAI_DEPLOYMENT=gpt-4o
|
||||
AZURE_OPENAI_EMBED_DEPLOYMENT=embed-3
|
||||
```
|
||||
|
||||
But when we try to access these deployments:
|
||||
```
|
||||
❌ Error 404: DeploymentNotFound
|
||||
"The API deployment for this resource does not exist"
|
||||
```
|
||||
|
||||
### Tested All Common Names - None Work
|
||||
|
||||
We automatically tested these deployment names:
|
||||
- Chat: `gpt-4`, `gpt-4o`, `gpt-35-turbo`, `gpt-4-32k`, `chat`, `gpt4`, `gpt4o`
|
||||
- Embeddings: `text-embedding-ada-002`, `text-embedding-3-small`, `embed`, `embed-3`, `ada-002`
|
||||
|
||||
**Result:** All returned 404
|
||||
|
||||
### What This Means
|
||||
|
||||
Either:
|
||||
1. **No deployments have been created yet** in your Azure OpenAI resource
|
||||
2. **Deployments have custom names** that we can't guess
|
||||
3. **API key doesn't have access** to the deployments
|
||||
|
||||
### How to Check
|
||||
|
||||
1. Go to Azure Portal: https://portal.azure.com
|
||||
2. Find your resource: `azureopenaiinstant.openai.azure.com`
|
||||
3. Click "Deployments" or "Model deployments"
|
||||
4. **Screenshot what you see** and share deployment names
|
||||
|
||||
---
|
||||
|
||||
## ✅ The Good News: Ollama Alternative
|
||||
|
||||
### Ollama is Available Locally
|
||||
|
||||
We found Ollama running on your server:
|
||||
- URL: `http://localhost:11434`
|
||||
- Chat model installed: `llama3.1:latest` ✅
|
||||
- Embedding model: `nomic-embed-text` (downloading now... ~260MB)
|
||||
|
||||
### What Ollama Can Do
|
||||
|
||||
| Capability | Status |
|
||||
|------------|--------|
|
||||
| Generate embeddings | ✅ Yes (once download completes) |
|
||||
| Vector search queries | ✅ Yes |
|
||||
| Generate chat responses | ✅ Yes (using llama3.1) |
|
||||
| **Cost** | ✅ **FREE** (runs locally) |
|
||||
|
||||
### Ollama vs. Azure OpenAI
|
||||
|
||||
| Feature | Ollama | Azure OpenAI |
|
||||
|---------|--------|--------------|
|
||||
| Cost | Free | Pay per token |
|
||||
| Speed | Fast (local) | Moderate (network) |
|
||||
| Quality | Good | Excellent |
|
||||
| Multilingual | Good | Excellent |
|
||||
| Configuration | ✅ Working now | ❌ Broken |
|
||||
|
||||
---
|
||||
|
||||
## 🎬 What Happens Next
|
||||
|
||||
### Option A: Use Ollama (Can Start Now)
|
||||
|
||||
**Pros:**
|
||||
- ✅ Already working on your server
|
||||
- ✅ Free (no API costs)
|
||||
- ✅ Fast (local processing)
|
||||
- ✅ Can generate embeddings for Romanian/Spanish/Italian Bibles
|
||||
|
||||
**Cons:**
|
||||
- ⚠️ Slightly lower quality than GPT-4
|
||||
- ⚠️ Requires local compute resources
|
||||
|
||||
**Implementation:**
|
||||
1. Wait for `nomic-embed-text` download to complete (~2 minutes)
|
||||
2. Update `.env.local` to prefer Ollama:
|
||||
```bash
|
||||
OLLAMA_API_URL=http://localhost:11434
|
||||
OLLAMA_EMBED_MODEL=nomic-embed-text
|
||||
```
|
||||
3. Create embeddings for Romanian/Spanish/Italian Bibles
|
||||
4. Chat will use `llama3.1` for responses
|
||||
|
||||
### Option B: Fix Azure OpenAI (Requires Azure Access)
|
||||
|
||||
**Pros:**
|
||||
- ✅ Higher quality responses (GPT-4)
|
||||
- ✅ Better multilingual support
|
||||
- ✅ Scalable for many users
|
||||
|
||||
**Cons:**
|
||||
- ❌ Costs money per API call
|
||||
- ❌ Requires Azure Portal access
|
||||
- ❌ Blocked until deployments are created
|
||||
|
||||
**Implementation:**
|
||||
1. Log into Azure Portal
|
||||
2. Go to Azure OpenAI resource
|
||||
3. Create two deployments:
|
||||
- Chat: Deploy `gpt-4` or `gpt-35-turbo` (name it anything)
|
||||
- Embeddings: Deploy `text-embedding-ada-002` or `text-embedding-3-small`
|
||||
4. Update `.env.local` with actual deployment names
|
||||
5. Test with our verification script
|
||||
|
||||
### Option C: Hybrid (Best of Both)
|
||||
|
||||
Use Ollama for embeddings (free) + Azure for chat (quality):
|
||||
|
||||
```bash
|
||||
# Use Ollama for embeddings
|
||||
OLLAMA_API_URL=http://localhost:11434
|
||||
OLLAMA_EMBED_MODEL=nomic-embed-text
|
||||
|
||||
# Use Azure for chat (once fixed)
|
||||
AZURE_OPENAI_DEPLOYMENT=<your-deployment-name>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Required Actions (In Order)
|
||||
|
||||
### Immediate (Today)
|
||||
|
||||
1. **Decision:** Choose Option A (Ollama), B (Azure), or C (Hybrid)
|
||||
|
||||
2. **If Ollama (Option A or C):**
|
||||
- ✅ Download is in progress
|
||||
- Wait 2-5 minutes for completion
|
||||
- Test with: `curl -X POST http://localhost:11434/api/embeddings -d '{"model":"nomic-embed-text","prompt":"test"}'`
|
||||
|
||||
3. **If Azure (Option B or C):**
|
||||
- Log into Azure Portal
|
||||
- Navigate to Azure OpenAI resource
|
||||
- Check/create deployments
|
||||
- Share deployment names
|
||||
|
||||
### Short-term (This Week)
|
||||
|
||||
4. **Get Romanian Bible Data:**
|
||||
- Source: `/bibles/Biblia-Fidela-limba-romana.md` (already exists!)
|
||||
- Need: Cornilescu version (if available)
|
||||
- Action: Create embeddings and import
|
||||
|
||||
5. **Get Spanish Bible Data:**
|
||||
- Source needed: RVR1960 (Reina-Valera 1960)
|
||||
- Optional: NVI (Nueva Versión Internacional)
|
||||
- Action: Find source, create embeddings, import
|
||||
|
||||
6. **Get Italian Bible Data:**
|
||||
- Source needed: Nuova Diodati
|
||||
- Optional: Nuova Riveduta
|
||||
- Action: Find source, create embeddings, import
|
||||
|
||||
### Medium-term (Next 2 Weeks)
|
||||
|
||||
7. **Implement English Fallback:**
|
||||
- When Romanian/Spanish/Italian searches return poor results
|
||||
- Automatically search English versions
|
||||
- Add language indicator in citations: `[KJV - English] John 3:16`
|
||||
|
||||
8. **Create Version Config Table:**
|
||||
- Track which versions are complete
|
||||
- Map versions to languages
|
||||
- Enable smart fallback logic
|
||||
|
||||
9. **Testing:**
|
||||
- Test Romanian queries → Romanian results
|
||||
- Test Spanish queries → Spanish results
|
||||
- Test Italian queries → Italian results
|
||||
- Test fallback when needed
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Technical Details
|
||||
|
||||
### Current Database Schema
|
||||
|
||||
Table naming pattern:
|
||||
```
|
||||
ai_bible.bv_{language_code}_{version_abbreviation}
|
||||
|
||||
Examples:
|
||||
- ai_bible.bv_en_eng_kjv ✅ Exists (English KJV)
|
||||
- ai_bible.bv_ro_cornilescu ❌ Needed (Romanian Cornilescu)
|
||||
- ai_bible.bv_es_rvr1960 ❌ Needed (Spanish RVR1960)
|
||||
- ai_bible.bv_it_nuovadiodati ❌ Needed (Italian Nuova Diodati)
|
||||
```
|
||||
|
||||
### Table Structure (All 116 tables have this)
|
||||
|
||||
| Column | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `id` | uuid | Primary key |
|
||||
| `testament` | text | OT/NT |
|
||||
| `book` | text | Book name |
|
||||
| `chapter` | integer | Chapter number |
|
||||
| `verse` | integer | Verse number |
|
||||
| `language` | text | Language code |
|
||||
| `translation` | text | Version abbreviation |
|
||||
| `ref` | text | "Genesis 1:1" format |
|
||||
| `text_raw` | text | Verse text |
|
||||
| `text_norm` | text | Normalized text |
|
||||
| `tsv` | tsvector | Full-text search index |
|
||||
| **`embedding`** | vector | **Vector embedding (3072 dims)** |
|
||||
| `created_at` | timestamp | Creation time |
|
||||
| `updated_at` | timestamp | Update time |
|
||||
|
||||
### Embedding Dimensions
|
||||
|
||||
Current `.env.local` says:
|
||||
```bash
|
||||
EMBED_DIMS=3072
|
||||
```
|
||||
|
||||
This matches:
|
||||
- ✅ Azure `text-embedding-3-small` (3072 dims)
|
||||
- ✅ Azure `text-embedding-3-large` (3072 dims)
|
||||
- ❌ Azure `text-embedding-ada-002` (1536 dims) - **INCOMPATIBLE**
|
||||
- ✅ Ollama `nomic-embed-text` (768 dims default, but can use 3072)
|
||||
|
||||
**Important:** If using Ollama, we may need to adjust embedding dimensions or re-create tables.
|
||||
|
||||
---
|
||||
|
||||
## 💡 Recommendations
|
||||
|
||||
### My Recommendation: Start with Ollama
|
||||
|
||||
**Why:**
|
||||
1. ✅ It's already working (or will be in 5 minutes)
|
||||
2. ✅ Free (no API costs while developing)
|
||||
3. ✅ Can immediately create Romanian embeddings from your `Fidela` Bible
|
||||
4. ✅ Unblocks development
|
||||
|
||||
**Then:**
|
||||
- Add Azure OpenAI later for higher quality (when deployments are fixed)
|
||||
- Use hybrid: Ollama for embeddings, Azure for chat
|
||||
|
||||
### Workflow I Suggest
|
||||
|
||||
```
|
||||
Today:
|
||||
→ Finish installing Ollama embedding model
|
||||
→ Test embedding generation
|
||||
→ Create embeddings for Fidela Romanian Bible
|
||||
→ Import into ai_bible.bv_ro_fidela
|
||||
→ Test Romanian chat
|
||||
|
||||
This Week:
|
||||
→ Fix Azure deployments (for better chat quality)
|
||||
→ Find Spanish RVR1960 data
|
||||
→ Find Italian Nuova Diodati data
|
||||
→ Create embeddings for both
|
||||
→ Import into database
|
||||
|
||||
Next Week:
|
||||
→ Implement English fallback
|
||||
→ Add version metadata table
|
||||
→ Create test suite
|
||||
→ Optimize performance
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📞 Questions for You
|
||||
|
||||
1. **AI Provider:** Do you want to use Ollama (free, local) or fix Azure OpenAI (better quality, costs money)?
|
||||
|
||||
2. **Azure Access:** Do you have access to the Azure Portal to check/create deployments?
|
||||
|
||||
3. **Bible Data:** Do you have Spanish (RVR1960) and Italian (Nuova Diodati) Bible data, or do we need to source it?
|
||||
|
||||
4. **Fidela Bible:** The file `./bibles/Biblia-Fidela-limba-romana.md` exists - should we create embeddings for this now?
|
||||
|
||||
5. **Embedding Dimensions:** Are you okay with potentially re-creating embedding tables with different dimensions if we switch from Azure (3072) to Ollama (768)?
|
||||
|
||||
---
|
||||
|
||||
## 📄 Reference Documents
|
||||
|
||||
| Document | Purpose | Location |
|
||||
|----------|---------|----------|
|
||||
| Implementation Plan | Detailed technical plan | `/AI_CHAT_FIX_PLAN.md` |
|
||||
| Verification Findings | Database analysis | `/AI_CHAT_VERIFICATION_FINDINGS.md` |
|
||||
| This Summary | Executive overview | `/AI_CHAT_ANALYSIS_SUMMARY.md` |
|
||||
| Verification Script | System health check | `/scripts/verify-ai-system.ts` |
|
||||
| Deployment Discovery | Find Azure deployments | `/scripts/discover-azure-deployments.ts` |
|
||||
|
||||
---
|
||||
|
||||
## ✅ Next Action
|
||||
|
||||
**Waiting for your decision:**
|
||||
- Option A: Use Ollama ← **Recommended to start**
|
||||
- Option B: Fix Azure OpenAI
|
||||
- Option C: Hybrid approach
|
||||
|
||||
Once you decide, I can immediately proceed with implementation.
|
||||
|
||||
---
|
||||
|
||||
**Status:** Analysis complete. Ready to implement based on your choice. 🚀
|
||||
1877
AI_CHAT_FIX_PLAN.md
Normal file
1877
AI_CHAT_FIX_PLAN.md
Normal file
File diff suppressed because it is too large
Load Diff
287
AI_CHAT_STATUS_UPDATE.md
Normal file
287
AI_CHAT_STATUS_UPDATE.md
Normal file
@@ -0,0 +1,287 @@
|
||||
# AI Chat System - Status Update
|
||||
|
||||
**Date:** 2025-10-10
|
||||
**Status:** ✅ Azure OpenAI Fixed | ⚠️ Need New Vector Tables
|
||||
|
||||
---
|
||||
|
||||
## 🎉 GOOD NEWS: Azure OpenAI is Working!
|
||||
|
||||
### ✅ What We Fixed
|
||||
|
||||
Both Azure OpenAI APIs are now **fully operational**:
|
||||
|
||||
| API | Status | Details |
|
||||
|-----|--------|---------|
|
||||
| **Chat API** | ✅ WORKING | GPT-4o responding correctly |
|
||||
| **Embedding API** | ✅ WORKING | text-embedding-ada-002 generating 1536-dim vectors |
|
||||
|
||||
**Updated Configuration:**
|
||||
```bash
|
||||
AZURE_OPENAI_ENDPOINT=https://footprints-ai.openai.azure.com
|
||||
AZURE_OPENAI_KEY=42702a67a41547919877a2ab8e4837f9
|
||||
|
||||
# Chat
|
||||
AZURE_OPENAI_DEPLOYMENT=gpt-4o
|
||||
AZURE_OPENAI_API_VERSION=2025-01-01-preview
|
||||
|
||||
# Embeddings
|
||||
AZURE_OPENAI_EMBED_DEPLOYMENT=Text-Embedding-ada-002-V2
|
||||
AZURE_OPENAI_EMBED_API_VERSION=2023-05-15
|
||||
EMBED_DIMS=1536
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ CRITICAL ISSUE: Embedding Dimension Mismatch
|
||||
|
||||
### The Problem
|
||||
|
||||
- **Existing 116 vector tables:** 4096-dimensional embeddings
|
||||
- **Our embedding model (ada-002):** 1536-dimensional embeddings
|
||||
- **Result:** **Cannot use existing tables** ❌
|
||||
|
||||
### What This Means
|
||||
|
||||
The 116 Bible versions currently in the database were created with a **different embedding model** (likely text-embedding-3-large with 4096 dims). We cannot search them with our 1536-dim embeddings because the dimensions must match exactly.
|
||||
|
||||
### The Solution
|
||||
|
||||
Create **new vector tables** for your priority languages with **1536-dim embeddings**:
|
||||
|
||||
1. ✅ **English** - Use existing Bible data (KJV, ASV, etc.)
|
||||
2. ❌ **Romanian** - Need Bible source data
|
||||
3. ❌ **Spanish** - Need Bible source data
|
||||
4. ❌ **Italian** - Need Bible source data
|
||||
|
||||
---
|
||||
|
||||
## 📋 What We Need To Do Next
|
||||
|
||||
### Option 1: Create New 1536-Dim Tables (RECOMMENDED)
|
||||
|
||||
**Pros:**
|
||||
- ✅ Works with our current Azure setup
|
||||
- ✅ Lower cost (ada-002 is cheaper than 3-large)
|
||||
- ✅ Faster searches (smaller vectors)
|
||||
- ✅ Sufficient quality for Bible search
|
||||
|
||||
**Steps:**
|
||||
1. Find/prepare Bible source data for each language
|
||||
2. Generate 1536-dim embeddings using our ada-002 deployment
|
||||
3. Create new tables: `bv_1536_ro_cornilescu`, `bv_1536_es_rvr1960`, etc.
|
||||
4. Import embeddings into new tables
|
||||
5. Update search logic to use new tables
|
||||
|
||||
### Option 2: Use Different Embedding Model (Not Recommended)
|
||||
|
||||
Deploy text-embedding-3-large (4096-dim) to match existing tables.
|
||||
|
||||
**Cons:**
|
||||
- ❌ Higher cost
|
||||
- ❌ Slower searches
|
||||
- ❌ Requires Azure deployment changes
|
||||
- ❌ Still missing Romanian/Spanish/Italian in existing tables
|
||||
|
||||
---
|
||||
|
||||
## 🗂️ Bible Source Data Status
|
||||
|
||||
### What We Have
|
||||
|
||||
✅ **Romanian (Fidela):** `/bibles/Biblia-Fidela-limba-romana.md`
|
||||
- Ready to process!
|
||||
- Can generate embeddings immediately
|
||||
|
||||
### What We Need
|
||||
|
||||
❌ **Romanian (Cornilescu):** Most popular Romanian version
|
||||
- Need to source this Bible translation
|
||||
- Options: Bible Gateway API, online sources, existing files
|
||||
|
||||
❌ **Spanish (RVR1960):** Most popular Spanish version
|
||||
- Reina-Valera 1960
|
||||
- Need to source
|
||||
|
||||
❌ **Italian (Nuova Diodati):** Popular Italian version
|
||||
- Need to source
|
||||
|
||||
❌ **English versions:** KJV, ASV, NIV, etc.
|
||||
- Can source from Bible Gateway, bible.org, or similar
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Recommended Next Steps
|
||||
|
||||
### Immediate (Today)
|
||||
|
||||
1. **Test the chat system** with a simple fallback:
|
||||
- Temporarily disable vector search
|
||||
- Have chat work without Bible verse context
|
||||
- Verify end-to-end flow is working
|
||||
|
||||
2. **Process Romanian Fidela Bible:**
|
||||
- Read `/bibles/Biblia-Fidela-limba-romana.md`
|
||||
- Parse into verse-by-verse format
|
||||
- Generate embeddings using ada-002
|
||||
- Create table `ai_bible.bv_1536_ro_fidela`
|
||||
- Import data
|
||||
|
||||
### Short-term (This Week)
|
||||
|
||||
3. **Source English Bible data:**
|
||||
- Download KJV (public domain)
|
||||
- Parse and generate embeddings
|
||||
- Create table `ai_bible.bv_1536_en_kjv`
|
||||
|
||||
4. **Source Romanian Cornilescu:**
|
||||
- Find public domain source
|
||||
- Parse and generate embeddings
|
||||
- Create table `ai_bible.bv_1536_ro_cornilescu`
|
||||
|
||||
5. **Source Spanish RVR1960:**
|
||||
- Find public domain source
|
||||
- Parse and generate embeddings
|
||||
- Create table `ai_bible.bv_1536_es_rvr1960`
|
||||
|
||||
6. **Source Italian Nuova Diodati:**
|
||||
- Find source
|
||||
- Parse and generate embeddings
|
||||
- Create table `ai_bible.bv_1536_it_nuovadiodati`
|
||||
|
||||
### Medium-term (Next 2 Weeks)
|
||||
|
||||
7. **Implement English Fallback Logic:**
|
||||
- Search primary language first
|
||||
- Fall back to English if results are poor
|
||||
- Add language indicators in citations
|
||||
|
||||
8. **Create Version Metadata Table:**
|
||||
- Track which versions are available
|
||||
- Map versions to languages
|
||||
- Enable smart version selection
|
||||
|
||||
9. **Testing & Optimization:**
|
||||
- Test all 4 languages
|
||||
- Optimize query performance
|
||||
- Add monitoring
|
||||
|
||||
---
|
||||
|
||||
## 📊 Database Schema for New Tables
|
||||
|
||||
### Table Naming Convention
|
||||
|
||||
```
|
||||
ai_bible.bv_1536_{language}_{version}
|
||||
|
||||
Examples:
|
||||
- ai_bible.bv_1536_en_kjv
|
||||
- ai_bible.bv_1536_ro_fidela
|
||||
- ai_bible.bv_1536_ro_cornilescu
|
||||
- ai_bible.bv_1536_es_rvr1960
|
||||
- ai_bible.bv_1536_it_nuovadiodati
|
||||
```
|
||||
|
||||
### Table Structure
|
||||
|
||||
```sql
|
||||
CREATE TABLE ai_bible.bv_1536_ro_fidela (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
testament TEXT NOT NULL, -- 'OT' or 'NT'
|
||||
book TEXT NOT NULL,
|
||||
chapter INTEGER NOT NULL,
|
||||
verse INTEGER NOT NULL,
|
||||
language TEXT NOT NULL, -- 'ro'
|
||||
translation TEXT NOT NULL, -- 'FIDELA'
|
||||
ref TEXT NOT NULL, -- 'Genesis 1:1'
|
||||
text_raw TEXT NOT NULL, -- Original verse text
|
||||
text_norm TEXT, -- Normalized for search
|
||||
tsv TSVECTOR, -- Full-text search index
|
||||
embedding VECTOR(1536), -- 1536-dimensional embedding
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create indexes
|
||||
CREATE INDEX idx_bv_1536_ro_fidela_ref ON ai_bible.bv_1536_ro_fidela(ref);
|
||||
CREATE INDEX idx_bv_1536_ro_fidela_book_chapter ON ai_bible.bv_1536_ro_fidela(book, chapter);
|
||||
CREATE INDEX idx_bv_1536_ro_fidela_tsv ON ai_bible.bv_1536_ro_fidela USING gin(tsv);
|
||||
CREATE INDEX idx_bv_1536_ro_fidela_embedding ON ai_bible.bv_1536_ro_fidela
|
||||
USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Implementation Script Needed
|
||||
|
||||
We need a script to:
|
||||
|
||||
1. **Parse Bible source file** (Markdown, JSON, CSV, etc.)
|
||||
2. **Generate embeddings** for each verse
|
||||
3. **Create table** if not exists
|
||||
4. **Insert verses** with embeddings
|
||||
5. **Create indexes**
|
||||
|
||||
**Example workflow:**
|
||||
```bash
|
||||
# Process Romanian Fidela Bible
|
||||
npx tsx scripts/import-bible.ts \
|
||||
--source ./bibles/Biblia-Fidela-limba-romana.md \
|
||||
--language ro \
|
||||
--translation FIDELA \
|
||||
--table bv_1536_ro_fidela
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 Quick Test - Chat Without Vector Search
|
||||
|
||||
To verify the chat system works end-to-end, we can temporarily:
|
||||
|
||||
1. Modify chat API to skip vector search
|
||||
2. Test chat with general biblical knowledge (GPT-4o has Bible knowledge)
|
||||
3. Verify authentication, conversation saving, and UI work
|
||||
4. Then add vector search back once tables are ready
|
||||
|
||||
**Would you like me to:**
|
||||
- ❓ Test chat without vector search first?
|
||||
- ❓ Start processing the Romanian Fidela Bible?
|
||||
- ❓ Create the Bible import script?
|
||||
- ❓ Something else?
|
||||
|
||||
---
|
||||
|
||||
## 📄 Files Updated
|
||||
|
||||
| File | Status | Purpose |
|
||||
|------|--------|---------|
|
||||
| `.env.local` | ✅ Updated | New Azure credentials, 1536 dims |
|
||||
| `lib/vector-search.ts` | ✅ Updated | Support separate embed API version |
|
||||
| `scripts/test-azure-quick.ts` | ✅ Created | Quick API testing |
|
||||
| `AI_CHAT_STATUS_UPDATE.md` | ✅ Created | This document |
|
||||
|
||||
---
|
||||
|
||||
## ✅ Summary
|
||||
|
||||
**What's Working:**
|
||||
- ✅ Azure OpenAI Chat (GPT-4o)
|
||||
- ✅ Azure OpenAI Embeddings (ada-002, 1536-dim)
|
||||
- ✅ Database connection
|
||||
- ✅ pgvector extension
|
||||
- ✅ Search code (just needs right tables)
|
||||
|
||||
**What's Blocked:**
|
||||
- ❌ Cannot use existing 116 tables (4096-dim vs 1536-dim mismatch)
|
||||
- ❌ Need new vector tables for Romanian/Spanish/Italian/English
|
||||
- ❌ Need Bible source data for Spanish and Italian
|
||||
|
||||
**Next Decision Point:**
|
||||
Choose what to do next:
|
||||
1. Test chat system without vector search (quick validation)
|
||||
2. Start creating vector tables with Fidela Romanian Bible (first language)
|
||||
3. Source and process English KJV (for fallback)
|
||||
4. All of the above in parallel
|
||||
|
||||
**Your call!** 🚀
|
||||
334
AI_CHAT_VERIFICATION_FINDINGS.md
Normal file
334
AI_CHAT_VERIFICATION_FINDINGS.md
Normal file
@@ -0,0 +1,334 @@
|
||||
# AI Chat System Verification Findings
|
||||
|
||||
**Date:** 2025-10-10
|
||||
**Status:** 🟡 Partially Operational - Configuration Issue Found
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The AI chat vector database is **fully operational** with 116 Bible versions across 47 languages, all with complete embeddings. However, there is a **critical configuration issue** with the Azure OpenAI API deployments that prevents the chat from functioning.
|
||||
|
||||
---
|
||||
|
||||
## ✅ What's Working
|
||||
|
||||
### 1. Vector Database Infrastructure (100% Operational)
|
||||
- **Database Connection:** PostgreSQL 17.5 ✓
|
||||
- **pgvector Extension:** v0.8.0 installed ✓
|
||||
- **Schema:** `ai_bible` schema exists ✓
|
||||
|
||||
### 2. Bible Vector Tables (116 Tables - Fully Populated)
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Total Vector Tables | **116** |
|
||||
| Languages Supported | **47** |
|
||||
| Embedding Coverage | **100%** for all tables |
|
||||
| Table Structure | Correct (all have embedding, tsv, ref, text_raw, etc.) |
|
||||
|
||||
**Sample Table Statistics:**
|
||||
- `bv_ab_aau`: 7,923 verses (100% embedded)
|
||||
- `bv_ac_aca`: 4,406 verses (100% embedded)
|
||||
- `bv_ac_acr_acc`: 7,930 verses (100% embedded)
|
||||
|
||||
### 3. Languages Available
|
||||
|
||||
The system currently supports **47 languages** including:
|
||||
- **English (en):** 9 versions (ASV, Brenton, KJV, KJV2006, LXX2012, RV, T4T, UK_LXX2012, WEB_C)
|
||||
- **German (de):** 2 versions
|
||||
- **Dutch (nl):** 3 versions
|
||||
- **French (fr):** 1 version
|
||||
- And 43+ other languages
|
||||
|
||||
**Note:** User requested support for **Romanian (ro), Spanish (es), and Italian (it)** but these languages are **NOT found** in the vector database. This is a critical gap.
|
||||
|
||||
### 4. Current Vector Search Implementation
|
||||
|
||||
The existing code in `/root/biblical-guide/lib/vector-search.ts` already implements:
|
||||
- ✅ Multi-table search across all versions for a given language
|
||||
- ✅ Hybrid search (vector + full-text)
|
||||
- ✅ Language-based table filtering
|
||||
- ✅ Proper query pattern: `bv_{lang}_{version}`
|
||||
|
||||
---
|
||||
|
||||
## ❌ What's Broken
|
||||
|
||||
### 1. Azure OpenAI API Configuration (CRITICAL)
|
||||
|
||||
**Problem:** The deployment names in `.env.local` do not exist in the Azure OpenAI resource.
|
||||
|
||||
**Environment Variables:**
|
||||
```bash
|
||||
AZURE_OPENAI_DEPLOYMENT=gpt-4o # ❌ Deployment NOT FOUND (404)
|
||||
AZURE_OPENAI_EMBED_DEPLOYMENT=embed-3 # ❌ Deployment NOT FOUND (404)
|
||||
```
|
||||
|
||||
**Error Message:**
|
||||
```
|
||||
DeploymentNotFound: The API deployment for this resource does not exist.
|
||||
```
|
||||
|
||||
**Impact:**
|
||||
- Chat API cannot generate responses
|
||||
- Embedding generation fails
|
||||
- Vector search cannot create query embeddings
|
||||
|
||||
### 2. Missing Priority Languages
|
||||
|
||||
**User Requirements:** Romanian (ro), Spanish (es), Italian (it)
|
||||
|
||||
**Current Status:**
|
||||
- ❌ **Romanian (ro):** NOT in vector database
|
||||
- ❌ **Spanish (es):** NOT in vector database
|
||||
- ❌ **Italian (it):** NOT in vector database
|
||||
|
||||
**Available Languages:** The current 47 languages are mostly obscure languages (ab, ac, ad, ag, etc.) and do NOT include the user's priority languages.
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Required Fixes
|
||||
|
||||
### Priority 1: Fix Azure OpenAI Deployments (IMMEDIATE)
|
||||
|
||||
**Action Required:**
|
||||
1. Identify the correct deployment names in the Azure OpenAI resource
|
||||
2. Update `.env.local` with correct values:
|
||||
- `AZURE_OPENAI_DEPLOYMENT=<actual-chat-deployment-name>`
|
||||
- `AZURE_OPENAI_EMBED_DEPLOYMENT=<actual-embedding-deployment-name>`
|
||||
|
||||
**Options to Find Correct Deployment Names:**
|
||||
- Option A: Check Azure Portal → Azure OpenAI → Deployments
|
||||
- Option B: Contact Azure admin who created the resource
|
||||
- Option C: Check deployment history/documentation
|
||||
|
||||
**Expected Deployment Patterns:**
|
||||
- Chat: Usually named like `gpt-4`, `gpt-4-32k`, `gpt-35-turbo`, etc.
|
||||
- Embeddings: Usually named like `text-embedding-ada-002`, `text-embedding-3-small`, etc.
|
||||
|
||||
### Priority 2: Add Priority Language Vector Tables (HIGH)
|
||||
|
||||
**Missing Tables Needed:**
|
||||
```sql
|
||||
-- Romanian versions
|
||||
ai_bible.bv_ro_cornilescu (Cornilescu Bible)
|
||||
ai_bible.bv_ro_fidela (Fidela Bible - mentioned in BIBLE_MD_PATH)
|
||||
|
||||
-- Spanish versions
|
||||
ai_bible.bv_es_rvr1960 (Reina-Valera 1960)
|
||||
ai_bible.bv_es_nvi (Nueva Versión Internacional)
|
||||
|
||||
-- Italian versions
|
||||
ai_bible.bv_it_nuovadiodati (Nuova Diodati)
|
||||
ai_bible.bv_it_nuovariveduta (Nuova Riveduta)
|
||||
```
|
||||
|
||||
**Action Required:**
|
||||
1. Verify if these Bible versions exist in source data
|
||||
2. Create embeddings for each version
|
||||
3. Import into `ai_bible` schema with proper naming
|
||||
|
||||
### Priority 3: Implement English Fallback (MEDIUM)
|
||||
|
||||
**Current Behavior:**
|
||||
- Search only looks in language-specific tables (e.g., only `bv_ro_*` for Romanian)
|
||||
- If language not found, returns empty results
|
||||
|
||||
**Required Behavior:**
|
||||
1. Search in primary language tables first
|
||||
2. Check result quality (min 3 results, top similarity > 0.75)
|
||||
3. If insufficient → fallback to English (`bv_en_*` tables)
|
||||
4. Return combined results with language indicators
|
||||
|
||||
**Implementation:** Already planned in `/root/biblical-guide/AI_CHAT_FIX_PLAN.md`
|
||||
|
||||
---
|
||||
|
||||
## 📊 Current System Architecture
|
||||
|
||||
### Vector Search Flow (Working)
|
||||
```
|
||||
User Query
|
||||
↓
|
||||
getEmbedding(query) ❌ FAILS HERE - Deployment Not Found
|
||||
↓
|
||||
searchBibleHybrid(query, language, limit)
|
||||
↓
|
||||
getAllVectorTables(language) ✓ Returns tables like ["ai_bible.bv_en_eng_kjv", ...]
|
||||
↓
|
||||
For each table:
|
||||
- Vector similarity search (embedding <=> query)
|
||||
- Full-text search (tsv @@ plainto_tsquery)
|
||||
- Combine scores (0.7 * vector + 0.3 * text)
|
||||
↓
|
||||
Sort by combined_score and return top results
|
||||
```
|
||||
|
||||
### Chat API Flow (Partially Working)
|
||||
```
|
||||
User Message
|
||||
↓
|
||||
[Auth Check] ✓ Working
|
||||
↓
|
||||
[Conversation Management] ✓ Working
|
||||
↓
|
||||
generateBiblicalResponse(message, locale, history)
|
||||
↓
|
||||
searchBibleHybrid(message, locale, 5) ❌ FAILS - Embedding API 404
|
||||
↓
|
||||
[Build Context with Verses] ✓ Would work if embeddings worked
|
||||
↓
|
||||
[Call Azure OpenAI Chat API] ❌ FAILS - Chat API 404
|
||||
↓
|
||||
[Save to Database] ✓ Working
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Implementation Plan
|
||||
|
||||
### Phase 1: Fix Azure OpenAI (Day 1 - URGENT)
|
||||
|
||||
1. **Identify Correct Deployments**
|
||||
- Check Azure Portal
|
||||
- List all available deployments in the resource
|
||||
- Document deployment names and models
|
||||
|
||||
2. **Update Environment Configuration**
|
||||
- Update `.env.local` with correct deployment names
|
||||
- Verify API version compatibility
|
||||
- Test connection with verification script
|
||||
|
||||
3. **Validate Fix**
|
||||
- Run `npx tsx scripts/verify-ai-system.ts`
|
||||
- Confirm both Chat API and Embedding API pass
|
||||
- Test end-to-end chat flow
|
||||
|
||||
### Phase 2: Add Priority Languages (Days 2-3)
|
||||
|
||||
1. **Romanian (ro)**
|
||||
- Source Bible data for Cornilescu and Fidela versions
|
||||
- Create embeddings using Azure OpenAI
|
||||
- Import into `ai_bible.bv_ro_cornilescu` and `ai_bible.bv_ro_fidela`
|
||||
|
||||
2. **Spanish (es)**
|
||||
- Source Bible data for RVR1960 and NVI
|
||||
- Create embeddings
|
||||
- Import into respective tables
|
||||
|
||||
3. **Italian (it)**
|
||||
- Source Bible data for Nuova Diodati and Nuova Riveduta
|
||||
- Create embeddings
|
||||
- Import into respective tables
|
||||
|
||||
### Phase 3: Implement Fallback Logic (Day 4)
|
||||
|
||||
1. **Update `searchBibleHybrid` Function**
|
||||
- Add quality check logic
|
||||
- Implement English fallback
|
||||
- Add language indicators to results
|
||||
|
||||
2. **Update Chat API Response**
|
||||
- Include source language in citations
|
||||
- Inform user when fallback was used
|
||||
- Format: `[KJV - English fallback] John 3:16`
|
||||
|
||||
### Phase 4: Testing (Day 5)
|
||||
|
||||
1. **Test Each Language**
|
||||
- Romanian queries → Romanian results
|
||||
- Spanish queries → Spanish results
|
||||
- Italian queries → Italian results
|
||||
- Unsupported language → English fallback
|
||||
|
||||
2. **Test Edge Cases**
|
||||
- Empty results handling
|
||||
- Mixed language queries
|
||||
- Very specific vs. general queries
|
||||
|
||||
3. **Performance Testing**
|
||||
- Query response time (target < 2s)
|
||||
- Multi-table search performance
|
||||
- Concurrent user handling
|
||||
|
||||
---
|
||||
|
||||
## 📝 Next Steps
|
||||
|
||||
### Immediate Actions (Today)
|
||||
|
||||
1. ✅ Run verification script (COMPLETED)
|
||||
2. ✅ Document findings (COMPLETED)
|
||||
3. 🔲 Fix Azure OpenAI deployment configuration
|
||||
- Identify correct deployment names
|
||||
- Update `.env.local`
|
||||
- Re-run verification script
|
||||
|
||||
### Short-term Actions (This Week)
|
||||
|
||||
4. 🔲 Source Romanian Bible data (Cornilescu, Fidela)
|
||||
5. 🔲 Source Spanish Bible data (RVR1960, NVI)
|
||||
6. 🔲 Source Italian Bible data (Nuova Diodati, Nuova Riveduta)
|
||||
7. 🔲 Create embeddings for all priority language versions
|
||||
8. 🔲 Import into vector database
|
||||
|
||||
### Medium-term Actions (Next 2 Weeks)
|
||||
|
||||
9. 🔲 Implement English fallback logic
|
||||
10. 🔲 Add version metadata table (`bible_version_config`)
|
||||
11. 🔲 Create comprehensive test suite
|
||||
12. 🔲 Monitor performance and optimize queries
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Critical Blockers
|
||||
|
||||
1. **Azure OpenAI Deployment Names** (Blocking ALL functionality)
|
||||
- Cannot generate embeddings
|
||||
- Cannot generate chat responses
|
||||
- Need Azure admin access to resolve
|
||||
|
||||
2. **Missing Priority Languages** (Blocking user requirements)
|
||||
- Romanian not available
|
||||
- Spanish not available
|
||||
- Italian not available
|
||||
- Need Bible data sources and embeddings pipeline
|
||||
|
||||
---
|
||||
|
||||
## 📈 Success Metrics
|
||||
|
||||
**Current Status:**
|
||||
- ✅ Database: 100%
|
||||
- ❌ API Configuration: 0%
|
||||
- ❌ Language Support: 0% (for priority languages)
|
||||
- ⚠️ Code Implementation: 80% (search logic exists, just needs API fix)
|
||||
|
||||
**Target Status:**
|
||||
- ✅ Database: 100%
|
||||
- ✅ API Configuration: 100%
|
||||
- ✅ Language Support: 100% (ro, es, it, en)
|
||||
- ✅ Code Implementation: 100%
|
||||
|
||||
---
|
||||
|
||||
## 📚 Reference Documents
|
||||
|
||||
- `/root/biblical-guide/AI_CHAT_FIX_PLAN.md` - Original implementation plan
|
||||
- `/root/biblical-guide/scripts/verify-ai-system.ts` - Verification script
|
||||
- `/root/biblical-guide/lib/vector-search.ts` - Current search implementation
|
||||
- `/root/biblical-guide/app/api/chat/route.ts` - Chat API implementation
|
||||
|
||||
---
|
||||
|
||||
## Contact & Support
|
||||
|
||||
**Azure OpenAI Resource:**
|
||||
- Endpoint: `https://azureopenaiinstant.openai.azure.com`
|
||||
- API Version: `2024-05-01-preview`
|
||||
- **Action Needed:** Verify deployment names in Azure Portal
|
||||
|
||||
**Vector Database:**
|
||||
- Host: `10.0.0.207:5432`
|
||||
- Database: `biblical-guide`
|
||||
- Schema: `ai_bible`
|
||||
- Status: ✅ Fully Operational
|
||||
@@ -9,6 +9,7 @@ import { OfflineBibleReader } from '@/components/bible/offline-bible-reader'
|
||||
import { offlineStorage } from '@/lib/offline-storage'
|
||||
import { InstallPrompt, useInstallPrompt } from '@/components/pwa/install-prompt'
|
||||
import { useSwipeable } from 'react-swipeable'
|
||||
import { AuthModal } from '@/components/auth/auth-modal'
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
@@ -315,6 +316,11 @@ export default function BibleReaderNew({ initialVersion, initialBook, initialCha
|
||||
verse: null
|
||||
})
|
||||
|
||||
// Auth modal state
|
||||
const [authModalOpen, setAuthModalOpen] = useState(false)
|
||||
const [authModalMessage, setAuthModalMessage] = useState<string>('')
|
||||
const [pendingAction, setPendingAction] = useState<(() => void) | null>(null)
|
||||
|
||||
// Refs
|
||||
const contentRef = useRef<HTMLDivElement>(null)
|
||||
const verseRefs = useRef<{[key: number]: HTMLDivElement}>({})
|
||||
@@ -990,6 +996,26 @@ export default function BibleReaderNew({ initialVersion, initialBook, initialCha
|
||||
}
|
||||
}
|
||||
|
||||
const requireAuth = (action: () => void, message: string) => {
|
||||
if (!user) {
|
||||
setAuthModalMessage(message)
|
||||
setPendingAction(() => action)
|
||||
setAuthModalOpen(true)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const handleAuthSuccess = () => {
|
||||
setAuthModalOpen(false)
|
||||
setAuthModalMessage('')
|
||||
// Execute pending action if there is one
|
||||
if (pendingAction) {
|
||||
pendingAction()
|
||||
setPendingAction(null)
|
||||
}
|
||||
}
|
||||
|
||||
const handlePreviousChapter = () => {
|
||||
// Trigger transition animation
|
||||
setIsTransitioning(true)
|
||||
@@ -1077,9 +1103,8 @@ export default function BibleReaderNew({ initialVersion, initialBook, initialCha
|
||||
const handleChapterBookmark = async () => {
|
||||
if (!selectedBook || !selectedChapter) return
|
||||
|
||||
// If user is not authenticated, redirect to login
|
||||
if (!user) {
|
||||
router.push(`/${locale}/login?redirect=${encodeURIComponent(`/${locale}/bible?version=${selectedVersion}&book=${selectedBook}&chapter=${selectedChapter}`)}`)
|
||||
// If user is not authenticated, show auth modal
|
||||
if (!requireAuth(handleChapterBookmark, 'Please login to bookmark this chapter')) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1123,9 +1148,8 @@ export default function BibleReaderNew({ initialVersion, initialBook, initialCha
|
||||
}
|
||||
|
||||
const handleVerseBookmark = async (verse: BibleVerse) => {
|
||||
// If user is not authenticated, redirect to login
|
||||
if (!user) {
|
||||
router.push(`/${locale}/login?redirect=${encodeURIComponent(`/${locale}/bible?version=${selectedVersion}&book=${selectedBook}&chapter=${selectedChapter}&verse=${verse.verseNum}`)}`)
|
||||
// If user is not authenticated, show auth modal
|
||||
if (!requireAuth(() => handleVerseBookmark(verse), 'Please login to bookmark this verse')) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1184,9 +1208,8 @@ export default function BibleReaderNew({ initialVersion, initialBook, initialCha
|
||||
}
|
||||
|
||||
const handleVerseChat = (verse: BibleVerse) => {
|
||||
// If user is not authenticated, redirect to login
|
||||
if (!user) {
|
||||
router.push(`/${locale}/login?redirect=${encodeURIComponent(`/${locale}/bible?version=${selectedVersion}&book=${selectedBook}&chapter=${selectedChapter}&verse=${verse.verseNum}`)}`)
|
||||
// If user is not authenticated, show auth modal
|
||||
if (!requireAuth(() => handleVerseChat(verse), 'Please login to ask AI about this verse')) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1255,9 +1278,8 @@ export default function BibleReaderNew({ initialVersion, initialBook, initialCha
|
||||
}
|
||||
|
||||
const handleHighlightVerse = async (verse: BibleVerse, color: TextHighlight['color']) => {
|
||||
// If user is not authenticated, redirect to login
|
||||
if (!user) {
|
||||
router.push(`/${locale}/login?redirect=${encodeURIComponent(`/${locale}/bible?version=${selectedVersion}&book=${selectedBook}&chapter=${selectedChapter}&verse=${verse.verseNum}`)}`)
|
||||
// If user is not authenticated, show auth modal
|
||||
if (!requireAuth(() => handleHighlightVerse(verse, color), 'Please login to highlight this verse')) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1397,8 +1419,8 @@ export default function BibleReaderNew({ initialVersion, initialBook, initialCha
|
||||
}
|
||||
|
||||
const handleSetFavoriteVersion = async () => {
|
||||
if (!user) {
|
||||
router.push(`/${locale}/login?redirect=${encodeURIComponent(`/${locale}/bible?version=${selectedVersion}&book=${selectedBook}&chapter=${selectedChapter}`)}`)
|
||||
// If user is not authenticated, show auth modal
|
||||
if (!requireAuth(handleSetFavoriteVersion, 'Please login to set your default Bible version')) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2667,6 +2689,19 @@ export default function BibleReaderNew({ initialVersion, initialBook, initialCha
|
||||
{copyFeedback.message}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
|
||||
{/* Auth Modal */}
|
||||
<AuthModal
|
||||
open={authModalOpen}
|
||||
onClose={() => {
|
||||
setAuthModalOpen(false)
|
||||
setAuthModalMessage('')
|
||||
setPendingAction(null)
|
||||
}}
|
||||
onSuccess={handleAuthSuccess}
|
||||
message={authModalMessage}
|
||||
defaultTab="login"
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@ import {
|
||||
ListItemText,
|
||||
MenuItem,
|
||||
useTheme,
|
||||
useMediaQuery,
|
||||
CircularProgress,
|
||||
Skeleton,
|
||||
Alert,
|
||||
@@ -29,6 +30,9 @@ import {
|
||||
Checkbox,
|
||||
SelectChangeEvent,
|
||||
Switch,
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
AccordionDetails,
|
||||
} from '@mui/material'
|
||||
import {
|
||||
Favorite,
|
||||
@@ -42,10 +46,12 @@ import {
|
||||
AutoAwesome,
|
||||
Edit,
|
||||
Login,
|
||||
ExpandMore,
|
||||
} from '@mui/icons-material'
|
||||
import { useState, useEffect, useMemo } from 'react'
|
||||
import { useTranslations, useLocale, useFormatter } from 'next-intl'
|
||||
import { useAuth } from '@/hooks/use-auth'
|
||||
import { AuthModal } from '@/components/auth/auth-modal'
|
||||
|
||||
interface PrayerRequest {
|
||||
id: string
|
||||
@@ -68,6 +74,7 @@ export default function PrayersPage() {
|
||||
const tc = useTranslations('common')
|
||||
const f = useFormatter()
|
||||
const { user } = useAuth()
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'))
|
||||
const [prayers, setPrayers] = useState<PrayerRequest[]>([])
|
||||
const [selectedCategory, setSelectedCategory] = useState<string>('all')
|
||||
const [openDialog, setOpenDialog] = useState(false)
|
||||
@@ -82,12 +89,14 @@ export default function PrayersPage() {
|
||||
const [isGenerating, setIsGenerating] = useState(false)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [viewMode, setViewMode] = useState<'private' | 'public'>(user ? 'private' : 'public')
|
||||
const [selectedLanguages, setSelectedLanguages] = useState<string[]>([locale])
|
||||
const [selectedLanguage, setSelectedLanguage] = useState<string>(locale)
|
||||
const [authModalOpen, setAuthModalOpen] = useState(false)
|
||||
|
||||
const languagesKey = useMemo(() => selectedLanguages.slice().sort().join(','), [selectedLanguages])
|
||||
const languageOptions = useMemo(() => ([
|
||||
{ value: 'en', label: t('languageFilter.options.en') },
|
||||
{ value: 'ro', label: t('languageFilter.options.ro') }
|
||||
{ value: 'ro', label: t('languageFilter.options.ro') },
|
||||
{ value: 'es', label: t('languageFilter.options.es') },
|
||||
{ value: 'it', label: t('languageFilter.options.it') }
|
||||
]), [t])
|
||||
const languageLabelMap = useMemo(() => (
|
||||
languageOptions.reduce((acc, option) => {
|
||||
@@ -106,21 +115,10 @@ export default function PrayersPage() {
|
||||
|
||||
useEffect(() => {
|
||||
if (viewMode === 'public') {
|
||||
setSelectedLanguages(prev => {
|
||||
if (prev.includes(locale)) {
|
||||
return prev
|
||||
}
|
||||
return [...prev, locale]
|
||||
})
|
||||
setSelectedLanguage(locale)
|
||||
}
|
||||
}, [locale, viewMode])
|
||||
|
||||
useEffect(() => {
|
||||
if (viewMode === 'public' && selectedLanguages.length === 0) {
|
||||
setSelectedLanguages([locale])
|
||||
}
|
||||
}, [viewMode, selectedLanguages, locale])
|
||||
|
||||
const categories = [
|
||||
{ value: 'personal', label: t('categories.personal'), color: 'primary' },
|
||||
{ value: 'family', label: t('categories.family'), color: 'secondary' },
|
||||
@@ -148,8 +146,7 @@ export default function PrayersPage() {
|
||||
params.append('visibility', viewMode)
|
||||
|
||||
if (viewMode === 'public') {
|
||||
const languagesToQuery = selectedLanguages.length > 0 ? selectedLanguages : [locale]
|
||||
languagesToQuery.forEach(lang => params.append('languages', lang))
|
||||
params.append('languages', selectedLanguage)
|
||||
}
|
||||
|
||||
const headers: Record<string, string> = {}
|
||||
@@ -185,7 +182,7 @@ export default function PrayersPage() {
|
||||
|
||||
useEffect(() => {
|
||||
fetchPrayers()
|
||||
}, [selectedCategory, user, viewMode, languagesKey])
|
||||
}, [selectedCategory, user, viewMode, selectedLanguage])
|
||||
|
||||
const handleGenerateAIPrayer = async () => {
|
||||
if (!aiPrompt.trim()) return
|
||||
@@ -225,14 +222,9 @@ export default function PrayersPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleLanguageChange = (event: SelectChangeEvent<string[]>) => {
|
||||
const handleLanguageChange = (event: SelectChangeEvent<string>) => {
|
||||
const value = event.target.value
|
||||
const parsed = typeof value === 'string'
|
||||
? value.split(',')
|
||||
: (value as string[])
|
||||
|
||||
const uniqueValues = Array.from(new Set(parsed.filter(Boolean)))
|
||||
setSelectedLanguages(uniqueValues)
|
||||
setSelectedLanguage(value)
|
||||
}
|
||||
|
||||
const handleSubmitPrayer = async () => {
|
||||
@@ -273,12 +265,18 @@ export default function PrayersPage() {
|
||||
|
||||
const handleOpenDialog = () => {
|
||||
if (!user) {
|
||||
// Could redirect to login or show login modal
|
||||
setAuthModalOpen(true)
|
||||
return
|
||||
}
|
||||
setOpenDialog(true)
|
||||
}
|
||||
|
||||
const handleAuthSuccess = () => {
|
||||
setAuthModalOpen(false)
|
||||
// After successful auth, open the add prayer dialog
|
||||
setOpenDialog(true)
|
||||
}
|
||||
|
||||
const handlePrayFor = async (prayerId: string) => {
|
||||
try {
|
||||
const headers: HeadersInit = {
|
||||
@@ -371,80 +369,101 @@ export default function PrayersPage() {
|
||||
fullWidth
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<Login />}
|
||||
onClick={() => {
|
||||
// Could redirect to login page or show login modal
|
||||
console.log('Please login to add prayers')
|
||||
}}
|
||||
startIcon={<Add />}
|
||||
onClick={handleOpenDialog}
|
||||
sx={{ mb: 3 }}
|
||||
>
|
||||
{locale === 'en' ? 'Login to Add Prayer' : 'Conectează-te pentru a adăuga'}
|
||||
{t('addPrayer')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t('categories.title')}
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||
<Chip
|
||||
label={t('categories.all')}
|
||||
color="default"
|
||||
variant={selectedCategory === 'all' ? 'filled' : 'outlined'}
|
||||
size="small"
|
||||
onClick={() => setSelectedCategory('all')}
|
||||
sx={{ justifyContent: 'flex-start', cursor: 'pointer' }}
|
||||
/>
|
||||
{categories.map((category) => (
|
||||
<Chip
|
||||
key={category.value}
|
||||
label={category.label}
|
||||
color={category.color as any}
|
||||
variant={selectedCategory === category.value ? 'filled' : 'outlined'}
|
||||
size="small"
|
||||
onClick={() => setSelectedCategory(category.value)}
|
||||
sx={{ justifyContent: 'flex-start', cursor: 'pointer' }}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
{/* Categories Accordion */}
|
||||
<Accordion defaultExpanded={!isMobile}>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMore />}
|
||||
aria-controls="categories-content"
|
||||
id="categories-header"
|
||||
>
|
||||
<Typography variant="h6">
|
||||
{t('categories.title')}
|
||||
</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||
<Chip
|
||||
label={t('categories.all')}
|
||||
color="default"
|
||||
variant={selectedCategory === 'all' ? 'filled' : 'outlined'}
|
||||
size="small"
|
||||
onClick={() => setSelectedCategory('all')}
|
||||
sx={{ justifyContent: 'flex-start', cursor: 'pointer' }}
|
||||
/>
|
||||
{categories.map((category) => (
|
||||
<Chip
|
||||
key={category.value}
|
||||
label={category.label}
|
||||
color={category.color as any}
|
||||
variant={selectedCategory === category.value ? 'filled' : 'outlined'}
|
||||
size="small"
|
||||
onClick={() => setSelectedCategory(category.value)}
|
||||
sx={{ justifyContent: 'flex-start', cursor: 'pointer' }}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
||||
{/* Language Filter Accordion */}
|
||||
{viewMode === 'public' && (
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<Typography variant="h6" sx={{ mb: 1 }}>
|
||||
{t('languageFilter.title')}
|
||||
</Typography>
|
||||
<FormControl fullWidth size="small">
|
||||
<Select
|
||||
multiple
|
||||
value={selectedLanguages}
|
||||
onChange={handleLanguageChange}
|
||||
renderValue={(selected) =>
|
||||
(selected as string[])
|
||||
.map(code => languageLabelMap[code] || code.toUpperCase())
|
||||
.join(', ')
|
||||
}
|
||||
>
|
||||
{languageOptions.map(option => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
<Checkbox checked={selectedLanguages.includes(option.value)} />
|
||||
<ListItemText primary={option.label} />
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mt: 1 }}>
|
||||
{t('languageFilter.helper')}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Accordion defaultExpanded={!isMobile} sx={{ mt: 2 }}>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMore />}
|
||||
aria-controls="language-content"
|
||||
id="language-header"
|
||||
>
|
||||
<Typography variant="h6">
|
||||
{t('languageFilter.title')}
|
||||
</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<FormControl fullWidth size="small">
|
||||
<Select
|
||||
value={selectedLanguage}
|
||||
onChange={handleLanguageChange}
|
||||
>
|
||||
{languageOptions.map(option => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: 'block' }}>
|
||||
{t('languageFilter.helper')}
|
||||
</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
)}
|
||||
|
||||
<Typography variant="h6" sx={{ mt: 3, mb: 1 }}>
|
||||
{t('stats.title')}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
• {t('stats.activeRequests', { count: prayers.length })}<br />
|
||||
• {t('stats.totalPrayers', { count: prayers.reduce((sum, p) => sum + p.prayerCount, 0) })}<br />
|
||||
• {t('stats.youPrayed', { count: prayers.filter(p => p.isPrayedFor).length })}
|
||||
</Typography>
|
||||
{/* Stats Accordion */}
|
||||
<Accordion defaultExpanded={!isMobile} sx={{ mt: 2 }}>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMore />}
|
||||
aria-controls="stats-content"
|
||||
id="stats-header"
|
||||
>
|
||||
<Typography variant="h6">
|
||||
{t('stats.title')}
|
||||
</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
• {t('stats.activeRequests', { count: prayers.length })}<br />
|
||||
• {t('stats.totalPrayers', { count: prayers.reduce((sum, p) => sum + p.prayerCount, 0) })}<br />
|
||||
• {t('stats.youPrayed', { count: prayers.filter(p => p.isPrayedFor).length })}
|
||||
</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
@@ -773,6 +792,15 @@ export default function PrayersPage() {
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
{/* Auth Modal */}
|
||||
<AuthModal
|
||||
open={authModalOpen}
|
||||
onClose={() => setAuthModalOpen(false)}
|
||||
onSuccess={handleAuthSuccess}
|
||||
message={t('authRequired')}
|
||||
defaultTab="login"
|
||||
/>
|
||||
</Container>
|
||||
</Box>
|
||||
)
|
||||
|
||||
334
components/auth/auth-modal.tsx
Normal file
334
components/auth/auth-modal.tsx
Normal file
@@ -0,0 +1,334 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
TextField,
|
||||
Button,
|
||||
Box,
|
||||
Typography,
|
||||
Alert,
|
||||
IconButton,
|
||||
Tabs,
|
||||
Tab,
|
||||
InputAdornment,
|
||||
CircularProgress
|
||||
} from '@mui/material'
|
||||
import { Close, Visibility, VisibilityOff } from '@mui/icons-material'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { useAuth } from '@/hooks/use-auth'
|
||||
|
||||
interface AuthModalProps {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
onSuccess?: () => void
|
||||
defaultTab?: 'login' | 'register'
|
||||
title?: string
|
||||
message?: string
|
||||
}
|
||||
|
||||
export function AuthModal({
|
||||
open,
|
||||
onClose,
|
||||
onSuccess,
|
||||
defaultTab = 'login',
|
||||
title,
|
||||
message
|
||||
}: AuthModalProps) {
|
||||
const t = useTranslations('auth')
|
||||
const { login, register } = useAuth()
|
||||
|
||||
const [tab, setTab] = useState<'login' | 'register'>(defaultTab)
|
||||
const [showPassword, setShowPassword] = useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
// Login form state
|
||||
const [loginData, setLoginData] = useState({
|
||||
email: '',
|
||||
password: ''
|
||||
})
|
||||
|
||||
// Register form state
|
||||
const [registerData, setRegisterData] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
confirmPassword: ''
|
||||
})
|
||||
|
||||
const handleTabChange = (event: React.SyntheticEvent, newValue: 'login' | 'register') => {
|
||||
setTab(newValue)
|
||||
setError(null)
|
||||
}
|
||||
|
||||
const handleLoginSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
setError(null)
|
||||
setLoading(true)
|
||||
|
||||
try {
|
||||
const success = await login(loginData.email, loginData.password)
|
||||
if (success) {
|
||||
// Clear form
|
||||
setLoginData({ email: '', password: '' })
|
||||
// Call success callback
|
||||
onSuccess?.()
|
||||
// Close modal
|
||||
onClose()
|
||||
} else {
|
||||
setError(t('loginError'))
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || t('connectionError'))
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleRegisterSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
setError(null)
|
||||
|
||||
// Validate passwords match
|
||||
if (registerData.password !== registerData.confirmPassword) {
|
||||
setError(t('passwordMismatch'))
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
|
||||
try {
|
||||
const success = await register(
|
||||
registerData.email,
|
||||
registerData.password,
|
||||
registerData.name || undefined
|
||||
)
|
||||
|
||||
if (success) {
|
||||
// Clear form
|
||||
setRegisterData({ name: '', email: '', password: '', confirmPassword: '' })
|
||||
// Call success callback
|
||||
onSuccess?.()
|
||||
// Close modal
|
||||
onClose()
|
||||
} else {
|
||||
setError(t('registerError'))
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || t('connectionError'))
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
if (!loading) {
|
||||
setError(null)
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
maxWidth="sm"
|
||||
fullWidth
|
||||
PaperProps={{
|
||||
sx: {
|
||||
borderRadius: 2
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogTitle sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', pb: 1 }}>
|
||||
<Typography variant="h6">
|
||||
{title || (tab === 'login' ? t('welcomeBack') : t('joinUs'))}
|
||||
</Typography>
|
||||
<IconButton
|
||||
onClick={handleClose}
|
||||
disabled={loading}
|
||||
size="small"
|
||||
sx={{ color: 'text.secondary' }}
|
||||
>
|
||||
<Close />
|
||||
</IconButton>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent>
|
||||
{message && (
|
||||
<Alert severity="info" sx={{ mb: 3 }}>
|
||||
{message}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Tabs
|
||||
value={tab}
|
||||
onChange={handleTabChange}
|
||||
variant="fullWidth"
|
||||
sx={{ mb: 3, borderBottom: 1, borderColor: 'divider' }}
|
||||
>
|
||||
<Tab label={t('login')} value="login" />
|
||||
<Tab label={t('register')} value="register" />
|
||||
</Tabs>
|
||||
|
||||
{error && (
|
||||
<Alert severity="error" sx={{ mb: 2 }}>
|
||||
{error}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{tab === 'login' ? (
|
||||
<Box component="form" onSubmit={handleLoginSubmit}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label={t('email')}
|
||||
type="email"
|
||||
value={loginData.email}
|
||||
onChange={(e) => setLoginData(prev => ({ ...prev, email: e.target.value }))}
|
||||
required
|
||||
disabled={loading}
|
||||
sx={{ mb: 2 }}
|
||||
autoComplete="email"
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label={t('password')}
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={loginData.password}
|
||||
onChange={(e) => setLoginData(prev => ({ ...prev, password: e.target.value }))}
|
||||
required
|
||||
disabled={loading}
|
||||
sx={{ mb: 3 }}
|
||||
autoComplete="current-password"
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
edge="end"
|
||||
disabled={loading}
|
||||
>
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
fullWidth
|
||||
size="large"
|
||||
disabled={loading}
|
||||
sx={{ mb: 2 }}
|
||||
>
|
||||
{loading ? <CircularProgress size={24} /> : t('login')}
|
||||
</Button>
|
||||
<Box sx={{ textAlign: 'center' }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{t('noAccount')}{' '}
|
||||
<Button
|
||||
onClick={() => setTab('register')}
|
||||
disabled={loading}
|
||||
sx={{ textTransform: 'none', p: 0, minWidth: 'auto' }}
|
||||
>
|
||||
{t('createAccount')}
|
||||
</Button>
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
<Box component="form" onSubmit={handleRegisterSubmit}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label={`${t('name')} ${t('optional')}`}
|
||||
type="text"
|
||||
value={registerData.name}
|
||||
onChange={(e) => setRegisterData(prev => ({ ...prev, name: e.target.value }))}
|
||||
disabled={loading}
|
||||
sx={{ mb: 2 }}
|
||||
autoComplete="name"
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label={t('email')}
|
||||
type="email"
|
||||
value={registerData.email}
|
||||
onChange={(e) => setRegisterData(prev => ({ ...prev, email: e.target.value }))}
|
||||
required
|
||||
disabled={loading}
|
||||
sx={{ mb: 2 }}
|
||||
autoComplete="email"
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label={t('password')}
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={registerData.password}
|
||||
onChange={(e) => setRegisterData(prev => ({ ...prev, password: e.target.value }))}
|
||||
required
|
||||
disabled={loading}
|
||||
sx={{ mb: 2 }}
|
||||
autoComplete="new-password"
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
edge="end"
|
||||
disabled={loading}
|
||||
>
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label={t('confirmPassword')}
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={registerData.confirmPassword}
|
||||
onChange={(e) => setRegisterData(prev => ({ ...prev, confirmPassword: e.target.value }))}
|
||||
required
|
||||
disabled={loading}
|
||||
sx={{ mb: 3 }}
|
||||
autoComplete="new-password"
|
||||
error={registerData.confirmPassword !== '' && registerData.password !== registerData.confirmPassword}
|
||||
helperText={
|
||||
registerData.confirmPassword !== '' && registerData.password !== registerData.confirmPassword
|
||||
? t('passwordMismatch')
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
fullWidth
|
||||
size="large"
|
||||
disabled={loading}
|
||||
sx={{ mb: 2 }}
|
||||
>
|
||||
{loading ? <CircularProgress size={24} /> : t('register')}
|
||||
</Button>
|
||||
<Box sx={{ textAlign: 'center' }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{t('alreadyHaveAccount')}{' '}
|
||||
<Button
|
||||
onClick={() => setTab('login')}
|
||||
disabled={loading}
|
||||
sx={{ textTransform: 'none', p: 0, minWidth: 'auto' }}
|
||||
>
|
||||
{t('login')}
|
||||
</Button>
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -45,6 +45,7 @@ import {
|
||||
import { useState, useRef, useEffect, useCallback } from 'react'
|
||||
import { useTranslations, useLocale } from 'next-intl'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import { AuthModal } from '@/components/auth/auth-modal'
|
||||
|
||||
interface ChatMessage {
|
||||
id: string
|
||||
@@ -95,6 +96,7 @@ export default function FloatingChat() {
|
||||
const [showRenameDialog, setShowRenameDialog] = useState(false)
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false)
|
||||
const [newTitle, setNewTitle] = useState('')
|
||||
const [authModalOpen, setAuthModalOpen] = useState(false)
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const scrollToBottom = () => {
|
||||
@@ -123,6 +125,15 @@ export default function FloatingChat() {
|
||||
return () => window.removeEventListener('floating-chat:open', handler as EventListener)
|
||||
}, [])
|
||||
|
||||
// Listen for auth sign-in required events
|
||||
useEffect(() => {
|
||||
const handler = () => {
|
||||
setAuthModalOpen(true)
|
||||
}
|
||||
window.addEventListener('auth:sign-in-required', handler as EventListener)
|
||||
return () => window.removeEventListener('auth:sign-in-required', handler as EventListener)
|
||||
}, [])
|
||||
|
||||
// Check authentication status
|
||||
useEffect(() => {
|
||||
checkAuthStatus()
|
||||
@@ -429,6 +440,12 @@ export default function FloatingChat() {
|
||||
|
||||
const toggleFullscreen = () => setIsFullscreen(prev => !prev)
|
||||
|
||||
const handleAuthSuccess = () => {
|
||||
setAuthModalOpen(false)
|
||||
// Re-check auth status to update the UI
|
||||
checkAuthStatus()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Floating Action Button */}
|
||||
@@ -999,6 +1016,17 @@ export default function FloatingChat() {
|
||||
)}
|
||||
</Paper>
|
||||
</Slide>
|
||||
|
||||
{/* Auth Modal */}
|
||||
<AuthModal
|
||||
open={authModalOpen}
|
||||
onClose={() => setAuthModalOpen(false)}
|
||||
onSuccess={handleAuthSuccess}
|
||||
message={locale === 'ro'
|
||||
? 'Vă rugăm să vă autentificați pentru a accesa chat-ul AI și a salva conversațiile.'
|
||||
: 'Please sign in to access the AI chat and save your conversations.'}
|
||||
defaultTab="login"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -78,8 +78,9 @@ export async function getEmbedding(text: string): Promise<number[]> {
|
||||
}
|
||||
|
||||
// Fallback to Azure OpenAI
|
||||
const embedApiVersion = process.env.AZURE_OPENAI_EMBED_API_VERSION || process.env.AZURE_OPENAI_API_VERSION
|
||||
const response = await fetch(
|
||||
`${process.env.AZURE_OPENAI_ENDPOINT}/openai/deployments/${process.env.AZURE_OPENAI_EMBED_DEPLOYMENT}/embeddings?api-version=${process.env.AZURE_OPENAI_API_VERSION}`,
|
||||
`${process.env.AZURE_OPENAI_ENDPOINT}/openai/deployments/${process.env.AZURE_OPENAI_EMBED_DEPLOYMENT}/embeddings?api-version=${embedApiVersion}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
||||
4
logo.svg
Normal file
4
logo.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="200" height="200">
|
||||
<path fill="#1976D2" d="M21 5c-1.11-.35-2.33-.5-3.5-.5-1.95 0-4.05.4-5.5 1.5-1.45-1.1-3.55-1.5-5.5-1.5S2.45 4.9 1 6v14.65c0 .25.25.5.5.5.1 0 .15-.05.25-.05C3.1 20.45 5.05 20 6.5 20c1.95 0 4.05.4 5.5 1.5 1.35-.85 3.8-1.5 5.5-1.5 1.65 0 3.35.3 4.75 1.05.1.05.15.05.25.05.25 0 .5-.25.5-.5V6c-.6-.45-1.25-.75-2-1m0 13.5c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5V8c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5z"/>
|
||||
<path fill="#1976D2" d="M17.5 10.5c.88 0 1.73.09 2.5.26V9.24c-.79-.15-1.64-.24-2.5-.24-1.7 0-3.24.29-4.5.83v1.66c1.13-.64 2.7-.99 4.5-.99M13 12.49v1.66c1.13-.64 2.7-.99 4.5-.99.88 0 1.73.09 2.5.26V11.9c-.79-.15-1.64-.24-2.5-.24-1.7 0-3.24.3-4.5.83m4.5 1.84c-1.7 0-3.24.29-4.5.83v1.66c1.13-.64 2.7-.99 4.5-.99.88 0 1.73.09 2.5.26v-1.52c-.79-.16-1.64-.24-2.5-.24"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 872 B |
@@ -269,6 +269,8 @@
|
||||
"prayers": {
|
||||
"title": "Prayers",
|
||||
"subtitle": "Share prayers and pray together with the community",
|
||||
"addPrayer": "Add Prayer",
|
||||
"authRequired": "Please sign in to add a prayer request and join our prayer community.",
|
||||
"viewModes": {
|
||||
"private": "My private prayers",
|
||||
"public": "Public prayer wall"
|
||||
@@ -279,7 +281,7 @@
|
||||
},
|
||||
"languageFilter": {
|
||||
"title": "Languages",
|
||||
"helper": "Choose which languages to include. Your current language stays selected.",
|
||||
"helper": "See prayers in other languages",
|
||||
"options": {
|
||||
"en": "English",
|
||||
"ro": "Romanian",
|
||||
|
||||
@@ -269,6 +269,8 @@
|
||||
"prayers": {
|
||||
"title": "Oraciones",
|
||||
"subtitle": "Comparte oraciones y ora junto con la comunidad",
|
||||
"addPrayer": "Agregar oración",
|
||||
"authRequired": "Por favor inicia sesión para agregar una oración y unirte a nuestra comunidad de oración.",
|
||||
"viewModes": {
|
||||
"private": "Mis oraciones privadas",
|
||||
"public": "Muro de oración público"
|
||||
@@ -279,7 +281,7 @@
|
||||
},
|
||||
"languageFilter": {
|
||||
"title": "Idiomas",
|
||||
"helper": "Elige qué idiomas incluir. Tu idioma actual permanece seleccionado.",
|
||||
"helper": "Ver oraciones en otros idiomas",
|
||||
"options": {
|
||||
"en": "Inglés",
|
||||
"ro": "Rumano",
|
||||
|
||||
@@ -269,6 +269,8 @@
|
||||
"prayers": {
|
||||
"title": "Preghiere",
|
||||
"subtitle": "Condividi preghiere e prega insieme alla comunità",
|
||||
"addPrayer": "Aggiungi preghiera",
|
||||
"authRequired": "Effettua l'accesso per aggiungere una preghiera e unirti alla nostra comunità di preghiera.",
|
||||
"viewModes": {
|
||||
"private": "Le mie preghiere private",
|
||||
"public": "Muro delle preghiere pubblico"
|
||||
@@ -279,7 +281,7 @@
|
||||
},
|
||||
"languageFilter": {
|
||||
"title": "Lingue",
|
||||
"helper": "Scegli quali lingue includere. La tua lingua corrente rimane selezionata.",
|
||||
"helper": "Vedi preghiere in altre lingue",
|
||||
"options": {
|
||||
"en": "Inglese",
|
||||
"ro": "Rumeno",
|
||||
|
||||
@@ -269,6 +269,8 @@
|
||||
"prayers": {
|
||||
"title": "Rugăciuni",
|
||||
"subtitle": "Partajează rugăciuni și roagă-te împreună cu comunitatea",
|
||||
"addPrayer": "Adaugă rugăciune",
|
||||
"authRequired": "Autentifică-te pentru a adăuga o rugăciune și a te alătura comunității noastre de rugăciune.",
|
||||
"viewModes": {
|
||||
"private": "Rugăciunile mele",
|
||||
"public": "Peretele de rugăciuni public"
|
||||
@@ -279,7 +281,7 @@
|
||||
},
|
||||
"languageFilter": {
|
||||
"title": "Limbi",
|
||||
"helper": "Alege limbile pentru care vrei să vezi rugăciuni. Limba curentă rămâne selectată.",
|
||||
"helper": "Vezi rugăciuni în alte limbi",
|
||||
"options": {
|
||||
"en": "Engleză",
|
||||
"ro": "Română",
|
||||
|
||||
Reference in New Issue
Block a user