diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx index 59c163a..0a7eac2 100644 --- a/app/[locale]/page.tsx +++ b/app/[locale]/page.tsx @@ -49,6 +49,33 @@ export default function Home() { const [userCount, setUserCount] = useState(2847) const [expandedFaq, setExpandedFaq] = useState(false) const [email, setEmail] = useState('') + const [dailyVerse, setDailyVerse] = useState<{ + date: string + verse: string + reference: string + } | null>(null) + + // Fetch daily verse + useEffect(() => { + const fetchDailyVerse = async () => { + try { + const response = await fetch(`/api/daily-verse?locale=${locale}`) + if (response.ok) { + const result = await response.json() + setDailyVerse(result.data) + } + } catch (error) { + console.error('Failed to fetch daily verse:', error) + // Fallback to static content if API fails + setDailyVerse({ + date: getCurrentDate(), + verse: t('dailyVerse.verse'), + reference: t('dailyVerse.reference') + }) + } + } + fetchDailyVerse() + }, [locale, t]) // Simulate live user counter useEffect(() => { @@ -229,15 +256,15 @@ export default function Home() { {t('dailyVerse.title')} - {getCurrentDate()} + {dailyVerse?.date || getCurrentDate()} - {t('dailyVerse.verse')} + {dailyVerse?.verse || t('dailyVerse.verse')} - {t('dailyVerse.reference')} + {dailyVerse?.reference || t('dailyVerse.reference')} @@ -245,8 +272,8 @@ export default function Home() { { - const verseText = t('dailyVerse.verse') - const reference = t('dailyVerse.reference') + const verseText = dailyVerse?.verse || t('dailyVerse.verse') + const reference = dailyVerse?.reference || t('dailyVerse.reference') const discussMessage = locale === 'ro' ? `Poți să îmi explici mai mult despre acest verset: "${verseText}" (${reference})?` : `Can you explain more about this verse: "${verseText}" (${reference})?` diff --git a/app/api/daily-verse/route.ts b/app/api/daily-verse/route.ts new file mode 100644 index 0000000..76cfd34 --- /dev/null +++ b/app/api/daily-verse/route.ts @@ -0,0 +1,137 @@ +import { NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' + +// Daily verses collection - you can expand this with more verses +const DAILY_VERSES = [ + { + verse: "Căci Eu știu gândurile pe care le am cu privire la voi, zice Domnul, gânduri de pace și nu de rău, ca să vă dau un viitor și o speranță.", + reference: "Ieremia 29:11", + verseEn: "For I know the plans I have for you, declares the Lord, plans to prosper you and not to harm you, plans to give you hope and a future.", + referenceEn: "Jeremiah 29:11" + }, + { + verse: "Încrede-te în Domnul din toată inima ta și nu te bizui pe înțelepciunea ta.", + reference: "Proverbe 3:5", + verseEn: "Trust in the Lord with all your heart and lean not on your own understanding.", + referenceEn: "Proverbs 3:5" + }, + { + verse: "Pot totul în Hristos care mă întărește.", + reference: "Filipeni 4:13", + verseEn: "I can do all things through Christ who strengthens me.", + referenceEn: "Philippians 4:13" + }, + { + verse: "Și știm că toate lucrurile lucrează împreună pentru binele celor ce iubesc pe Dumnezeu.", + reference: "Romani 8:28", + verseEn: "And we know that in all things God works for the good of those who love him.", + referenceEn: "Romans 8:28" + }, + { + verse: "Nu te teme, căci Eu sunt cu tine; nu te uita cu îngrijorare, căci Eu sunt Dumnezeul tău!", + reference: "Isaia 41:10", + verseEn: "So do not fear, for I am with you; do not be dismayed, for I am your God.", + referenceEn: "Isaiah 41:10" + }, + { + verse: "Domnul este păstorul meu: nu voi duce lipsă de nimic.", + reference: "Psalm 23:1", + verseEn: "The Lord is my shepherd, I lack nothing.", + referenceEn: "Psalm 23:1" + }, + { + verse: "Bucurați-vă totdeauna în Domnul! Iarăși zic: Bucurați-vă!", + reference: "Filipeni 4:4", + verseEn: "Rejoice in the Lord always. I will say it again: Rejoice!", + referenceEn: "Philippians 4:4" + } +] + +function getDailyVerse(date: Date, locale: string = 'ro') { + // Calculate day of year to ensure the same verse appears all day + const start = new Date(date.getFullYear(), 0, 0) + const diff = date.getTime() - start.getTime() + const dayOfYear = Math.floor(diff / (1000 * 60 * 60 * 24)) + + // Use day of year to select verse consistently + const verseIndex = dayOfYear % DAILY_VERSES.length + const selectedVerse = DAILY_VERSES[verseIndex] + + const formatDate = (date: Date, locale: string) => { + if (locale === 'ro') { + return date.toLocaleDateString('ro-RO', { + year: 'numeric', + month: 'long', + day: 'numeric' + }) + } else { + return date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric' + }) + } + } + + return { + date: formatDate(date, locale), + verse: locale === 'ro' ? selectedVerse.verse : selectedVerse.verseEn, + reference: locale === 'ro' ? selectedVerse.reference : selectedVerse.referenceEn, + dayOfYear, + verseIndex + } +} + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url) + const locale = searchParams.get('locale') || 'ro' + + // Validate locale + if (!['ro', 'en'].includes(locale)) { + return NextResponse.json( + { error: 'Invalid locale. Must be "ro" or "en".' }, + { status: 400 } + ) + } + + const today = new Date() + const dailyVerse = getDailyVerse(today, locale) + + return NextResponse.json({ + success: true, + data: dailyVerse + }) + + } catch (error) { + console.error('Daily verse API error:', error) + return NextResponse.json( + { + error: 'Failed to get daily verse', + message: error instanceof Error ? error.message : 'Unknown error' + }, + { status: 500 } + ) + } +} + +// Optional: Add POST endpoint to add new verses (for admin use) +export async function POST(request: NextRequest) { + try { + const body = await request.json() + + // This would typically save to database + // For now, just return success + return NextResponse.json({ + success: true, + message: 'Daily verse system is read-only for now' + }) + + } catch (error) { + console.error('Daily verse POST error:', error) + return NextResponse.json( + { error: 'Failed to process request' }, + { status: 500 } + ) + } +} \ No newline at end of file diff --git a/temp/Ghid Biblic – Improved Homepage Content & SEO Setup - RO_EN.md b/temp/Ghid Biblic – Improved Homepage Content & SEO Setup - RO_EN.md new file mode 100644 index 0000000..46261ec --- /dev/null +++ b/temp/Ghid Biblic – Improved Homepage Content & SEO Setup - RO_EN.md @@ -0,0 +1,235 @@ +````markdown +# Unified SEO & Content Strategy for **Ghid Biblic / Biblical Guide** + +This document provides: +1. Unified homepage texts (Romanian + English, SEO-optimized) +2. Unified SEO `` setup with hreflang cross-linking +3. Best practices to avoid duplicate content penalties + +--- + +## 1. Homepage Texts + +### Romanian (RO) + +#### H1 +**Ghid Biblic – Studiu biblic online cu AI, versete zilnice și comunitate de rugăciune** + +#### Hero +Ghid Biblic este o aplicație de studiu biblic online. Citește Scriptura, pune întrebări cu ajutorul chatului AI, caută versete rapid și alătură-te unei comunități de rugăciune care te sprijină zilnic. + +**CTA:** +Încearcă acum gratuit - Chat AI + +#### Social Proof +Alătură-te miilor de credincioși care folosesc Ghid Biblic pentru a înțelege și aplica Cuvântul lui Dumnezeu în viața de zi cu zi. + +#### Daily Verse +*Versetul biblic al zilei – primește zilnic inspirație din Scriptură direct în inbox.* + +> Căci Eu știu gândurile pe care le am cu privire la voi, zice Domnul, gânduri de pace și nu de rău, ca să vă dau un viitor și o speranță. +> *Ieremia 29:11* + +#### Features +- **Citește Biblia online** – toate cele 66 de cărți biblice într-o interfață modernă și intuitivă +- **Chat AI biblic** – întreabă Scriptura și primește răspunsuri clare, fundamentate pe versete +- **Comunitate de rugăciune** – trimite și primește cereri de rugăciune +- **Căutare versete** – găsește rapid pasaje, cuvinte cheie și teme biblice + +#### Footer +Ghid Biblic – aplicație de studiu biblic online, cu chat AI, versete zilnice și comunitate de rugăciune. + +--- + +### English (EN) + +#### H1 +**Biblical Guide – Online Bible Study with AI Chat, Daily Verses, and Prayer Community** + +#### Hero +Biblical Guide is an online Bible study app. Read Scripture, ask questions with AI-powered chat, search verses instantly, and join a global prayer community that supports your spiritual growth. + +**CTA:** +Try it free now – AI Bible chat + +#### Social Proof +Join thousands of believers who use Biblical Guide to study, understand, and apply God’s Word in their everyday lives. + +#### Daily Verse +*Daily Bible Verse – receive encouragement from Scripture every day, straight to your inbox.* + +> “For I know the plans I have for you,” declares the Lord, “plans to prosper you and not to harm you, plans to give you hope and a future.” +> *Jeremiah 29:11* + +#### Features +- **Read the Bible online** – access all 66 books with a modern and intuitive interface +- **AI Bible Chat** – ask Scripture questions and get clear, accurate answers +- **Prayer Community** – share requests and join others in prayer +- **Verse Search** – quickly find verses, keywords, and topics across the Bible + +#### Footer +Biblical Guide – online Bible study app with AI chat, daily verses, and prayer community. + +--- + +## 2. Unified SEO `` Setup + +### Meta Titles +- RO: +```html +Ghid Biblic – Studiu Biblic Online cu AI, Versete Zilnice și Comunitate de Rugăciune +```` + +* EN: + +```html +Biblical Guide – Online Bible Study with AI, Daily Verses & Prayer Community +``` + +--- + +### Meta Descriptions + +* RO: + +```html + +``` + +* EN: + +```html + +``` + +--- + +### Keywords + +*(Optional, modern SEO engines use them less but safe to include)* + +* RO: + +```html + +``` + +* EN: + +```html + +``` + +--- + +### Canonical + +Both languages point to their own canonical URL: + +```html + + +``` + +--- + +### Hreflang Cross-Linking + +```html + + + +``` + +--- + +### Open Graph (OG) + +* RO: + +```html + + + + +``` + +* EN: + +```html + + + + +``` + +--- + +### Twitter Card + +```html + + +``` + +RO version: + +```html + + + +``` + +EN version: + +```html + + + +``` + +--- + +### Schema.org (JSON-LD) + +Single schema with multiple languages: + +```json + +``` + +--- + +## 3. Best Practices to Avoid Duplicate Content + +1. **Hreflang implementation** – Ensure Romanian and English pages point to each other. +2. **Separate URLs per language** – e.g., `/ro/` and `/en/`. +3. **Canonical tags per version** – Each language version should self-reference. +4. **Localized metadata** – Meta title, description, and OG tags must be language-specific. +5. **X-default landing page** – The root (`/`) can act as a language selector or default (EN). +6. **Consistent translations** – Avoid auto-translating SEO tags; use tailored keywords per language. + +--- + +✅ With this setup, search engines will correctly index both RO and EN versions, serve the right one to users based on language/region, and avoid duplicate content penalties. + +``` diff --git a/temp/admin-dashboard-mvp.md b/temp/admin-dashboard-mvp.md new file mode 100644 index 0000000..c99c468 --- /dev/null +++ b/temp/admin-dashboard-mvp.md @@ -0,0 +1,347 @@ +# BiblicalGuide Admin Dashboard - MVP Features + +## 1. Dashboard Overview (Home) + +### Key Metrics Cards +- **Total Users** (number + % change from last week) +- **Daily Active Users** (DAU - real-time count) +- **AI Conversations Today** (count + cost estimate) +- **Active Now** (users currently online) + +### Quick Stats +- **New signups** (last 24 hours) +- **Total bookmarks created** (all-time) +- **Prayer requests** (last 24 hours) +- **Server status** (API health, response time) + +### Activity Feed (Live) +- New user registrations +- Prayer wall posts +- High-engagement AI conversations +- Error alerts +- System notifications + +## 2. User Management + +### User List View +``` +Search: [_________] Filter: [All Users ▼] + +| Email | Name | Joined | Last Active | AI Chats | Status | Actions | +|-------|------|--------|-------------|----------|--------|---------| +| user@example.com | John D. | 2024-01-15 | 2 hrs ago | 45 | Active | [View] [Ban] | +``` + +### User Actions +- **View Profile** (see detailed user activity) +- **Send Email** (direct communication) +- **Reset Password** (force password reset) +- **Suspend/Ban** (temporary or permanent) +- **Delete Account** (GDPR compliance) + +### User Details Modal +- Registration date and method +- Total AI conversations +- Bookmarks count +- Last 10 chat topics +- Prayer requests posted +- Account status history + +## 3. AI Chat Monitoring + +### Conversations Overview +- **Total conversations** (today/week/month) +- **Average response time** (target: <5 seconds) +- **Error rate** (failed responses %) +- **Cost tracking** (OpenAI API spend) + +### Live Chat Monitor +``` +Recent Conversations (Auto-refresh every 30s) + +User: "What does the Bible say about forgiveness?" +AI: "The Bible speaks extensively about forgiveness..." +[View Full] [Flag] [Quality: ⭐⭐⭐⭐⭐] + +User: "Help me understand Romans 8" +AI: "Romans 8 is one of the most powerful chapters..." +[View Full] [Flag] [Quality: ⭐⭐⭐⭐⭐] +``` + +### Quality Control +- **Flag inappropriate requests** +- **Review AI responses** for theological accuracy +- **Common questions** (FAQ generation) +- **Failed responses** log +- **Response ratings** (if users rate them) + +### Cost Management +- Daily API usage ($X.XX) +- Projection for current month +- Per-user average cost +- High-usage user alerts + +## 4. Content Moderation + +### Prayer Wall Moderation +``` +Pending Review (3) + +"Please pray for my family..." +[Approve] [Edit] [Reject] [Ban User] + +"Spam content here..." +[Approve] [Edit] [Reject] [Ban User] +``` + +### Moderation Queue +- **Prayer requests** (pending approval) +- **Reported content** (user flags) +- **AI conversation flags** (inappropriate) +- **Bulk actions** (approve/reject all) + +### Auto-Moderation Settings +- Profanity filter (on/off) +- Spam detection threshold +- Auto-approve trusted users +- Keyword blacklist management + +## 5. Analytics Dashboard + +### User Analytics +- **Growth Chart** (daily new users - line graph) +- **Retention Funnel** + - Day 1: 100% + - Day 7: 45% + - Day 30: 30% +- **User Segments** + - Power users (>10 chats/week) + - Regular (3-10 chats/week) + - Casual (<3 chats/week) + - Dormant (no activity 7+ days) + +### Engagement Metrics +- **Most Read Bible Books** (top 10 bar chart) +- **Popular Chat Topics** (word cloud) +- **Peak Usage Hours** (heatmap) +- **Feature Usage** (bookmarks vs chat vs prayer wall) + +### Simple Conversion Tracking +- Sign-up to first chat +- First chat to bookmark +- Single to returning user +- Free to premium (when implemented) + +## 6. System Administration + +### Quick Actions +- **Broadcast Message** (banner to all users) +- **Maintenance Mode** (on/off toggle) +- **Clear Cache** (Redis flush) +- **Backup Database** (manual trigger) + +### API Management +- **OpenAI API Status** + - Current balance + - Rate limit status + - Error rate + - Switch API keys +- **Bible API Status** + - Response time + - Cache hit rate + - Error logs + +### Error Monitoring +``` +Recent Errors (Last 24 hours) + +Type | Count | Last Occurred | Status +-----|-------|---------------|-------- +API Timeout | 12 | 10 min ago | [View] [Resolve] +Login Failed | 45 | 2 min ago | [View] [Ignore] +DB Connection | 0 | - | OK +``` + +### Server Health +- CPU usage (%) +- Memory usage (%) +- Database connections +- API response times +- Disk space remaining + +## 7. Communications + +### Email Templates (Basic) +- Welcome email +- Password reset +- Daily verse (if enabled) +- Account suspended +- System announcements + +### Bulk Email +- **Send to**: All users / Active last 7 days / Segment +- **Subject**: [_________] +- **Message**: [Rich text editor] +- **Schedule**: Now / Later +- [Send Test] [Schedule] [Send Now] + +### In-App Notifications +- Create announcement banner +- Target specific user segments +- Set expiration time +- Track dismissal rate + +## 8. Settings & Configuration + +### App Settings +- **Site name**: BiblicalGuide +- **Support email**: support@biblical-guide.com +- **Daily verse**: Enabled/Disabled +- **Prayer wall**: Public/Moderated/Disabled +- **AI model**: GPT-4/GPT-3.5 +- **Rate limits**: X chats per user per day + +### Feature Toggles +- [ ] AI Chat enabled +- [ ] Prayer Wall enabled +- [ ] User registration open +- [ ] Daily verse emails +- [ ] Social sharing +- [ ] Maintenance mode + +### Admin Users +``` +Admin Users (3) + +Email | Role | Last Login | Actions +------|------|------------|-------- +admin@site.com | Super Admin | 1 hr ago | [Edit] [Remove] +support@site.com | Moderator | 2 days ago | [Edit] [Remove] +[+ Add Admin] +``` + +## 9. Simple Reporting + +### Daily Report Email +- New users count +- Total AI conversations +- API costs +- Error summary +- Top chat topics +- System health status + +### Export Data +- **User list** (CSV) +- **Chat logs** (last 30 days) +- **Prayer requests** (CSV) +- **Analytics summary** (PDF) + +## 10. Security Section + +### Security Log +``` +Recent Security Events + +Time | User | Event | IP Address | Action +-----|------|-------|------------|-------- +10:23 AM | john@... | Failed login (3x) | 192.168.1.1 | [Block IP] +09:45 AM | Admin login | Success | 10.0.0.1 | - +``` + +### Security Actions +- View failed login attempts +- IP blocking/allowlisting +- Force logout all users +- Require password reset (bulk) +- 2FA enforcement settings + +## Implementation Priority + +### Phase 1 - Critical (Week 1) +1. **Login/Authentication** for admins +2. **Dashboard overview** (basic metrics) +3. **User list** with basic actions +4. **AI conversation monitoring** (view only) + +### Phase 2 - Essential (Week 2) +1. **Prayer wall moderation** +2. **User management** (suspend/ban) +3. **Basic analytics** (users, engagement) +4. **Error monitoring** + +### Phase 3 - Important (Week 3) +1. **Email communications** +2. **System settings** +3. **Export functionality** +4. **Cost tracking** + +## Tech Stack for Admin + +### Frontend +- **React** with **Recharts** for graphs +- **Tailwind CSS** with **shadcn/ui** components +- **React Table** for data tables +- **React Query** for data fetching + +### Backend Additions +- **Admin authentication** (separate from users) +- **Role-based access** (admin, moderator) +- **Audit logging** (track all admin actions) +- **Scheduled jobs** (daily reports) + +### Database Schema Additions +```sql +-- Admin users table +admin_users ( + id, email, password_hash, role, + last_login, created_at +) + +-- Audit log table +audit_logs ( + id, admin_id, action, target_user_id, + details, ip_address, timestamp +) + +-- System settings table +settings ( + key, value, updated_at, updated_by +) +``` + +## Access Control Levels + +### Super Admin +- Full access to everything +- Can manage other admins +- System configuration +- Database operations + +### Moderator +- Content moderation +- User management (not deletion) +- View analytics +- Cannot change system settings + +### Support +- View user details +- Reset passwords +- View chat logs +- Cannot ban/delete users + +## Security Considerations + +1. **Separate admin authentication** (not regular user accounts) +2. **IP allowlisting** for admin access +3. **Audit log** all admin actions +4. **Session timeout** after 30 min inactive +5. **2FA required** for all admin accounts +6. **Read-only mode** for most views (explicit edit mode) + +## Success Metrics for Admin + +- **Response time** to user issues < 1 hour +- **Moderation queue** cleared daily +- **System uptime** > 99.9% +- **API costs** within budget +- **User complaints** resolved < 24 hours \ No newline at end of file diff --git a/temp/ai-chat-improvements-plan.md b/temp/ai-chat-improvements-plan.md new file mode 100644 index 0000000..7c5d277 --- /dev/null +++ b/temp/ai-chat-improvements-plan.md @@ -0,0 +1,364 @@ +# AI Chat Improvements Plan + +## Overview +Enhance the AI chat system with persistent chat history and conversation memory to provide a more engaging and contextual user experience. + +## Current State Analysis +- ✅ Basic AI chat with Azure OpenAI integration +- ✅ ReactMarkdown rendering for formatted responses +- ✅ Floating chat component with fullscreen mode +- ✅ Language-specific responses (Romanian/English) +- ❌ No chat persistence between sessions +- ❌ Limited conversation memory (only last 3 messages) +- ❌ No user-specific chat history +- ❌ No chat management UI + +## Goals +1. **Persistent Chat History**: Store chat conversations in database per user and language +2. **Enhanced Memory**: Maintain longer conversation context for better AI responses +3. **Chat Management**: Allow users to view, organize, and manage their chat history +4. **Multi-language Support**: Separate chat histories for Romanian and English + +## Technical Implementation Plan + +### 1. Database Schema Extensions + +#### 1.1 Chat Conversations Table +```prisma +model ChatConversation { + id String @id @default(cuid()) + userId String? // Optional for anonymous users + title String // Auto-generated from first message + language String // 'ro' or 'en' + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + lastMessageAt DateTime @default(now()) + + user User? @relation(fields: [userId], references: [id], onDelete: Cascade) + messages ChatMessage[] + + @@index([userId, language, lastMessageAt]) + @@index([isActive, lastMessageAt]) +} + +model ChatMessage { + id String @id @default(cuid()) + conversationId String + role ChatMessageRole + content String @db.Text + timestamp DateTime @default(now()) + metadata Json? // For storing additional context + + conversation ChatConversation @relation(fields: [conversationId], references: [id], onDelete: Cascade) + + @@index([conversationId, timestamp]) +} + +enum ChatMessageRole { + USER + ASSISTANT + SYSTEM +} +``` + +#### 1.2 Update User Model +```prisma +model User { + // ... existing fields + chatConversations ChatConversation[] +} +``` + +### 2. API Endpoints + +#### 2.1 Chat Conversations API (`/api/chat/conversations`) +- **GET**: List user's conversations (paginated, filtered by language) +- **POST**: Create new conversation +- **DELETE /:id**: Delete conversation + +#### 2.2 Enhanced Chat API (`/api/chat`) +- **Modify existing POST**: Include conversation management +- **GET /:conversationId**: Get conversation history +- **PUT /:conversationId**: Update conversation (rename, etc.) + +#### 2.3 Chat Messages API (`/api/chat/:conversationId/messages`) +- **GET**: Get all messages in conversation (paginated) +- **POST**: Add message to conversation + +### 3. Frontend Components + +#### 3.1 Enhanced Floating Chat Component +```typescript +interface FloatingChatProps { + conversationId?: string + initialMessage?: string +} + +// New state management +const [conversations, setConversations] = useState([]) +const [activeConversationId, setActiveConversationId] = useState(null) +const [messages, setMessages] = useState([]) +const [isHistoryOpen, setIsHistoryOpen] = useState(false) +``` + +#### 3.2 Chat History Sidebar +```typescript +interface ChatHistorySidebarProps { + conversations: Conversation[] + activeConversationId: string | null + onSelectConversation: (id: string) => void + onNewConversation: () => void + onDeleteConversation: (id: string) => void + language: string +} +``` + +#### 3.3 Conversation List Item +```typescript +interface ConversationListItemProps { + conversation: Conversation + isActive: boolean + onClick: () => void + onDelete: () => void + onRename: (newTitle: string) => void +} +``` + +### 4. Implementation Phases + +#### Phase 1: Database Schema & Basic API +1. **Create Prisma migrations** for new tables +2. **Implement conversation CRUD APIs** +3. **Add database seed scripts** for testing +4. **Update existing chat API** to work with conversations + +#### Phase 2: Enhanced Memory System +1. **Modify chat API** to include more conversation context +2. **Implement intelligent context selection** (last 10-15 messages) +3. **Add conversation summarization** for very long chats +4. **Optimize token usage** with smart context management + +#### Phase 3: Frontend Chat Management +1. **Add conversation state management** to floating chat +2. **Implement chat history sidebar** +3. **Add conversation creation/deletion** functionality +4. **Implement conversation switching** within chat + +#### Phase 4: Advanced Features +1. **Auto-generate conversation titles** from first message +2. **Add conversation search/filtering** +3. **Implement conversation sharing** (optional) +4. **Add conversation export** functionality + +### 5. Detailed Implementation Steps + +#### 5.1 Database Setup +```bash +# Create migration +npx prisma migrate dev --name add-chat-conversations + +# Update database +npx prisma db push + +# Generate client +npx prisma generate +``` + +#### 5.2 API Implementation Strategy + +**Chat API Enhancement (`/api/chat/route.ts`)**: +```typescript +// Modified request schema +const chatRequestSchema = z.object({ + message: z.string().min(1), + conversationId: z.string().optional(), + locale: z.string().default('ro'), + userId: z.string().optional() +}) + +// Enhanced response +interface ChatResponse { + success: boolean + response: string + conversationId: string + messageId: string +} +``` + +**Conversation Management Logic**: +```typescript +async function handleChatMessage( + message: string, + conversationId?: string, + locale: string = 'ro', + userId?: string +) { + // 1. Get or create conversation + const conversation = conversationId + ? await getConversation(conversationId) + : await createConversation(userId, locale, message) + + // 2. Get conversation history (last 15 messages) + const history = await getConversationHistory(conversation.id, 15) + + // 3. Generate AI response with full context + const aiResponse = await generateBiblicalResponse(message, locale, history) + + // 4. Save both user message and AI response + await saveMessages(conversation.id, [ + { role: 'user', content: message }, + { role: 'assistant', content: aiResponse } + ]) + + // 5. Update conversation metadata + await updateConversationActivity(conversation.id) + + return { response: aiResponse, conversationId: conversation.id } +} +``` + +#### 5.3 Frontend State Management + +**Enhanced Floating Chat Hook**: +```typescript +function useFloatingChat() { + const [conversations, setConversations] = useState([]) + const [activeConversation, setActiveConversation] = useState(null) + const [messages, setMessages] = useState([]) + const [isLoading, setIsLoading] = useState(false) + + const loadConversations = useCallback(async () => { + // Fetch user's conversations + }, []) + + const switchConversation = useCallback(async (conversationId: string) => { + // Load conversation messages + }, []) + + const startNewConversation = useCallback(() => { + // Reset state for new conversation + }, []) + + const sendMessage = useCallback(async (message: string) => { + // Send message with conversation context + }, [activeConversation]) + + return { + conversations, + activeConversation, + messages, + isLoading, + loadConversations, + switchConversation, + startNewConversation, + sendMessage + } +} +``` + +### 6. UI/UX Enhancements + +#### 6.1 Chat History Sidebar Layout +``` +┌─────────────────┬──────────────────────┐ +│ Chat History │ Active Chat │ +│ │ │ +│ ○ New Chat │ ┌─ Messages ─────┐ │ +│ │ │ │ │ +│ ╔═ Today ═══ │ │ User: Question │ │ +│ ║ ○ Bible Q&A │ │ AI: Response │ │ +│ ║ ○ Prayer Help │ │ ... │ │ +│ ╚═══════════ │ │ │ │ +│ │ └────────────────┘ │ +│ ╔═ Yesterday ═ │ ┌─ Input ──────────┐ │ +│ ║ ○ Verse Study │ │ [Type message... ]│ │ +│ ╚═══════════ │ └──────────────────┘ │ +└─────────────────┴──────────────────────┘ +``` + +#### 6.2 Mobile-Responsive Design +- **Mobile**: Stack history as overlay/drawer +- **Tablet**: Side-by-side with collapsed history +- **Desktop**: Full side-by-side layout + +### 7. Performance Considerations + +#### 7.1 Database Optimization +- **Indexes**: conversation lookups, message pagination +- **Pagination**: Limit conversation and message queries +- **Cleanup**: Archive old conversations after 6 months + +#### 7.2 Frontend Optimization +- **Lazy loading**: Load conversations on demand +- **Virtualization**: For large message lists +- **Caching**: Cache conversation metadata + +#### 7.3 AI Context Management +- **Smart truncation**: Summarize old messages if context too long +- **Relevance scoring**: Prioritize recent and relevant messages +- **Token optimization**: Balance context richness vs cost + +### 8. Security & Privacy + +#### 8.1 Data Protection +- **User isolation**: Strict user-based access control +- **Encryption**: Sensitive conversation content +- **Retention policy**: Auto-delete after configurable period + +#### 8.2 Anonymous Users +- **Session-based storage**: For non-authenticated users +- **Migration path**: Convert anonymous chats to user accounts + +### 9. Testing Strategy + +#### 9.1 Unit Tests +- Conversation CRUD operations +- Message history management +- AI context generation + +#### 9.2 Integration Tests +- Full chat flow with persistence +- Conversation switching +- Multi-language handling + +#### 9.3 User Testing +- Chat history navigation +- Conversation management +- Mobile responsiveness + +### 10. Deployment Considerations + +#### 10.1 Migration Strategy +- **Backward compatibility**: Existing chat continues to work +- **Data migration**: Convert existing session data if applicable +- **Feature flags**: Gradual rollout of new features + +#### 10.2 Monitoring +- **Conversation metrics**: Creation, length, engagement +- **Performance monitoring**: API response times +- **Error tracking**: Failed chat operations + +## Success Metrics + +1. **User Engagement**: Longer chat sessions, return conversations +2. **Conversation Quality**: Better AI responses with context +3. **User Satisfaction**: Positive feedback on chat experience +4. **Technical Performance**: Fast conversation loading, reliable persistence + +## Timeline Estimate + +- **Phase 1** (Database & API): 3-4 days +- **Phase 2** (Enhanced Memory): 2-3 days +- **Phase 3** (Frontend Management): 4-5 days +- **Phase 4** (Advanced Features): 3-4 days + +**Total**: ~2-3 weeks for complete implementation + +## Next Steps + +1. Review and approve this plan +2. Begin with Phase 1: Database schema and basic API +3. Implement incremental improvements +4. Test thoroughly at each phase +5. Gather user feedback and iterate \ No newline at end of file diff --git a/temp/azure-embed3-bible-pgvector-guide.md b/temp/azure-embed3-bible-pgvector-guide.md new file mode 100644 index 0000000..6cce4d5 --- /dev/null +++ b/temp/azure-embed3-bible-pgvector-guide.md @@ -0,0 +1,372 @@ +# Azure OpenAI **embed-3** → Postgres + pgvector Ingestion Guide (Bible Corpus) + +**Goal**: Create a production‑ready Python script that ingests the full Bible (Markdown source) into **Postgres** with **pgvector** and **full‑text** metadata, using **Azure OpenAI `embed-3`** embeddings. The vectors will power a consumer chat assistant (Q&A & conversations about the Bible) and a backend agent that generates custom prayers. + +> Sample corpus used here: Romanian *Biblia Fidela* (Markdown). Structure contains books, chapters, verses (e.g., *Geneza 1:1…*) and a TOC in the file. fileciteturn0file0 + +--- + +## 0) Architecture at a glance + +- **Input**: Bible in Markdown (`*.md`) → parser → normalized records: *(book, chapter, verse, text, lang=ro)* +- **Embedding**: Azure OpenAI **embed-3** (prefer `text-embedding-3-large`, 3072‑D). Batch inputs to cut cost/latency. +- **Storage**: Postgres with: + - `pgvector` column `embedding vector(3072)` + - `tsvector` column for hybrid lexical search (Romanian or English config as needed) + - metadata columns for fast filtering (book, chapter, verse, testament, translation, language) +- **Indexes**: `ivfflat` over `embedding`, GIN over `tsv` (and btree over metadata) +- **Retrieval**: + - Dense vector kNN + - Hybrid: combine kNN score + BM25/tsvector + - Windowed context stitching (neighbor verses) for chat +- **Consumers**: + - Chat assistant: answer + cite (book:chapter:verse). + - Prayer agent: prompt‑compose with retrieved passages & user intents. + +--- + +## 1) Prerequisites + +### Postgres + pgvector +```bash +# Install pgvector (on Ubuntu) +sudo apt-get update && sudo apt-get install -y postgresql postgresql-contrib +# In psql as superuser: +CREATE EXTENSION IF NOT EXISTS vector; +``` + +### Python deps +```bash +python -m venv .venv && source .venv/bin/activate +pip install psycopg[binary] pgvector pydantic python-dotenv httpx tqdm rapidfuzz +``` + +> `httpx` for HTTP (async‑capable), `pgvector` adapter, `rapidfuzz` for optional de‑dup or heuristic joins, `tqdm` for progress. + +### Azure OpenAI +- Create **Embeddings** deployment for **`text-embedding-3-large`** (or `-small` if cost sensitive). Name it (e.g.) `embeddings`. +- Collect: + - `AZURE_OPENAI_ENDPOINT=https://.openai.azure.com/` + - `AZURE_OPENAI_API_KEY=...` + - `AZURE_OPENAI_API_VERSION=2024-05-01-preview` *(or your current stable)* + - `AZURE_OPENAI_EMBED_DEPLOYMENT=embeddings` *(your deployment name)* + +Create `.env`: +```env +DATABASE_URL=postgresql://user:pass@localhost:5432/bible +AZURE_OPENAI_ENDPOINT=https://YOUR_RESOURCE.openai.azure.com/ +AZURE_OPENAI_API_KEY=YOUR_KEY +AZURE_OPENAI_API_VERSION=2024-05-01-preview +AZURE_OPENAI_EMBED_DEPLOYMENT=embeddings +EMBED_DIMS=3072 +BIBLE_MD_PATH=./Biblia-Fidela-limba-romana.md +LANG_CODE=ro +TRANSLATION_CODE=FIDELA +``` + +--- + +## 2) Database schema + +```sql +-- One-time setup in your database +CREATE EXTENSION IF NOT EXISTS vector; + +CREATE TABLE IF NOT EXISTS bible_passages ( + id BIGSERIAL PRIMARY KEY, + testament TEXT NOT NULL, -- 'OT' or 'NT' + book TEXT NOT NULL, + chapter INT NOT NULL, + verse INT NOT NULL, + ref TEXT GENERATED ALWAYS AS (book || ' ' || chapter || ':' || verse) STORED, + lang TEXT NOT NULL DEFAULT 'ro', + translation TEXT NOT NULL DEFAULT 'FIDELA', + text_raw TEXT NOT NULL, -- exact verse text + text_norm TEXT NOT NULL, -- normalized/cleaned text (embedding input) + tsv tsvector, + embedding vector(3072), -- 1536 if using embed-3-small + created_at TIMESTAMPTZ DEFAULT now(), + updated_at TIMESTAMPTZ DEFAULT now() +); + +-- Uniqueness by canonical reference within translation/language +CREATE UNIQUE INDEX IF NOT EXISTS ux_ref_lang ON bible_passages (translation, lang, book, chapter, verse); + +-- Full-text index (choose config; Romanian available if installed via ISPELL; else use 'simple' or 'english') +-- If you have pg_catalog.romanian, use that. Else fallback to 'simple' but keep lexemes. +CREATE INDEX IF NOT EXISTS idx_tsv ON bible_passages USING GIN (tsv); + +-- Vector index (choose nlist to match data size; we set after populating table) +-- First create a flat index for small data, or IVFFLAT for scale: +-- Requires ANALYZE beforehand and SET enable_seqscan=off for kNN plans. +``` + +After loading, build the IVFFLAT index (the table must be populated first): +```sql +-- Example: around 31k verses ⇒ nlist ~ 100–200 is reasonable; tune per EXPLAIN ANALYZE +CREATE INDEX IF NOT EXISTS idx_vec_ivfflat +ON bible_passages USING ivfflat (embedding vector_cosine_ops) +WITH (lists = 200); +``` + +Trigger to keep `updated_at` fresh: +```sql +CREATE OR REPLACE FUNCTION touch_updated_at() RETURNS TRIGGER AS $$ +BEGIN NEW.updated_at = now(); RETURN NEW; END; $$ LANGUAGE plpgsql; + +DROP TRIGGER IF EXISTS trg_bible_updated ON bible_passages; +CREATE TRIGGER trg_bible_updated BEFORE UPDATE ON bible_passages +FOR EACH ROW EXECUTE PROCEDURE touch_updated_at(); +``` + +--- + +## 3) Parsing & Chunking strategy (large, high‑quality) + +**Why verse‑level?** It’s the canonical granular unit for Bible QA. +**Context‑stitching**: during retrieval, fetch neighbor verses (±N) to maintain narrative continuity. +**Normalization** steps (for `text_norm`): +- Strip verse numbers and sidenotes if present in raw lines. +- Collapse whitespace, unify quotes, remove page headers/footers and TOC artifacts. +- Preserve punctuation; avoid stemming before embeddings. +- Lowercasing optional (OpenAI embeddings are case-robust). + +**Testament/book detection**: From headings and TOC present in the Markdown; detect Book → Chapter → Verse boundaries via regex. +Example regex heuristics (tune to your file): +- Book headers: `^(?P[A-ZĂÂÎȘȚ].+?)\s*$` (bounded by known canon order) +- Chapter headers: `^Capitolul\s+(?P\d+)` or `^CApitoLuL\s+(?P\d+)` (case variations) +- Verse lines: `^(?P\d+)\s+(.+)$` + +> The provided Markdown clearly shows book order (e.g., *Geneza*, *Exodul*, …; NT: *Matei*, *Marcu*, …) and verse lines like “**1** LA început…”. fileciteturn0file0 + +--- + +## 4) Python ingestion script + +> **Save as** `ingest_bible_pgvector.py` + +```python +import os, re, json, math, time, asyncio +from typing import List, Dict, Tuple, Iterable +from dataclasses import dataclass +from pathlib import Path +from dotenv import load_dotenv +import httpx +import psycopg +from psycopg.rows import dict_row + +load_dotenv() + +AZ_ENDPOINT = os.getenv("AZURE_OPENAI_ENDPOINT", "").rstrip("/") +AZ_API_KEY = os.getenv("AZURE_OPENAI_API_KEY") +AZ_API_VER = os.getenv("AZURE_OPENAI_API_VERSION", "2024-05-01-preview") +AZ_DEPLOYMENT = os.getenv("AZURE_OPENAI_EMBED_DEPLOYMENT", "embeddings") +EMBED_DIMS = int(os.getenv("EMBED_DIMS", "3072")) +DB_URL = os.getenv("DATABASE_URL") +BIBLE_MD_PATH = os.getenv("BIBLE_MD_PATH") +LANG_CODE = os.getenv("LANG_CODE", "ro") +TRANSLATION = os.getenv("TRANSLATION_CODE", "FIDELA") + +assert AZ_ENDPOINT and AZ_API_KEY and DB_URL and BIBLE_MD_PATH, "Missing required env vars" + +EMBED_URL = f"{AZ_ENDPOINT}/openai/deployments/{AZ_DEPLOYMENT}/embeddings?api-version={AZ_API_VER}" + +BOOKS_OT = [ + "Geneza","Exodul","Leviticul","Numeri","Deuteronom","Iosua","Judecători","Rut", + "1 Samuel","2 Samuel","1 Imparati","2 Imparati","1 Cronici","2 Cronici","Ezra","Neemia","Estera", + "Iov","Psalmii","Proverbe","Eclesiastul","Cântarea Cântărilor","Isaia","Ieremia","Plângerile", + "Ezechiel","Daniel","Osea","Ioel","Amos","Obadia","Iona","Mica","Naum","Habacuc","Țefania","Hagai","Zaharia","Maleahi" +] +BOOKS_NT = [ + "Matei","Marcu","Luca","Ioan","Faptele Apostolilor","Romani","1 Corinteni","2 Corinteni", + "Galateni","Efeseni","Filipeni","Coloseni","1 Tesaloniceni","2 Tesaloniceni","1 Timotei","2 Timotei", + "Titus","Filimon","Evrei","Iacov","1 Petru","2 Petru","1 Ioan","2 Ioan","3 Ioan","Iuda","Revelaţia" +] + +BOOK_CANON = {b:("OT" if b in BOOKS_OT else "NT") for b in BOOKS_OT + BOOKS_NT} + +@dataclass +class Verse: + testament: str + book: str + chapter: int + verse: int + text_raw: str + text_norm: str + +def normalize_text(s: str) -> str: + s = re.sub(r"\s+", " ", s.strip()) + s = s.replace(" ", " ") + return s + +BOOK_RE = re.compile(r"^(?P[A-ZĂÂÎȘȚ][^\n]+?)\s*$") +CH_RE = re.compile(r"^(?i:Capitolul|CApitoLuL)\s+(?P\d+)\b") +VERSE_RE = re.compile(r"^(?P\d+)\s+(?P.+)$") + +def parse_bible_md(md_text: str): + cur_book, cur_ch = None, None + testament = None + for line in md_text.splitlines(): + line = line.rstrip() + + # Book detection + m_book = BOOK_RE.match(line) + if m_book: + bname = m_book.group("book").strip() + if bname in BOOK_CANON: + cur_book = bname + testament = BOOK_CANON[bname] + cur_ch = None + continue + + m_ch = CH_RE.match(line) + if m_ch and cur_book: + cur_ch = int(m_ch.group("ch")) + continue + + m_v = VERSE_RE.match(line) + if m_v and cur_book and cur_ch: + vnum = int(m_v.group("v")) + body = m_v.group("body").strip() + raw = body + norm = normalize_text(body) + yield { + "testament": testament, "book": cur_book, "chapter": cur_ch, "verse": vnum, + "text_raw": raw, "text_norm": norm + } + +async def embed_batch(client, inputs): + payload = {"input": inputs} + headers = {"api-key": AZ_API_KEY, "Content-Type": "application/json"} + for attempt in range(6): + try: + r = await client.post(EMBED_URL, headers=headers, json=payload, timeout=60) + if r.status_code == 200: + data = r.json() + ordered = sorted(data["data"], key=lambda x: x["index"]) + return [d["embedding"] for d in ordered] + elif r.status_code in (429, 500, 503): + backoff = 2 ** attempt + (0.1 * attempt) + await asyncio.sleep(backoff) + else: + raise RuntimeError(f"Embedding error {r.status_code}: {r.text}") + except Exception: + backoff = 2 ** attempt + (0.1 * attempt) + await asyncio.sleep(backoff) + raise RuntimeError("Failed to embed after retries") + +UPSERT_SQL = """ +INSERT INTO bible_passages (testament, book, chapter, verse, lang, translation, text_raw, text_norm, tsv, embedding) +VALUES (%(testament)s, %(book)s, %(chapter)s, %(verse)s, %(lang)s, %(translation)s, %(text_raw)s, %(text_norm)s, + to_tsvector(COALESCE(%(ts_lang)s,'simple')::regconfig, %(text_norm)s), %(embedding)s) +ON CONFLICT (translation, lang, book, chapter, verse) DO UPDATE +SET text_raw=EXCLUDED.text_raw, + text_norm=EXCLUDED.text_norm, + tsv=EXCLUDED.tsv, + embedding=EXCLUDED.embedding, + updated_at=now(); +""" + +async def main(): + md_text = Path(BIBLE_MD_PATH).read_text(encoding="utf-8", errors="ignore") + verses = list(parse_bible_md(md_text)) + print(f"Parsed verses: {len(verses)}") + + batch_size = 128 + async with httpx.AsyncClient() as client, psycopg.connect(DB_URL, autocommit=False) as conn: + with conn.cursor() as cur: + for i in range(0, len(verses), batch_size): + batch = verses[i:i+batch_size] + inputs = [v["text_norm"] for v in batch] + embs = await embed_batch(client, inputs) + rows = [] + for v, e in zip(batch, embs): + rows.append({ + **v, + "lang": os.getenv("LANG_CODE","ro"), + "translation": os.getenv("TRANSLATION_CODE","FIDELA"), + "ts_lang": "romanian", + "embedding": e + }) + cur.executemany(UPSERT_SQL, rows) + conn.commit() + print(f"Upserted {len(rows)} … {i+len(rows)}/{len(verses)}") + print("Done. Build IVFFLAT index after ANALYZE.") + +if __name__ == "__main__": + import asyncio + asyncio.run(main()) +``` + +**Notes** +- If `romanian` text search config is unavailable, set `ts_lang='simple'`. +- For `embed-3-small`, set `EMBED_DIMS=1536` and change column type to `vector(1536)`. + +--- + +## 5) Post‑ingestion steps + +```sql +VACUUM ANALYZE bible_passages; +CREATE INDEX IF NOT EXISTS idx_vec_ivfflat +ON bible_passages USING ivfflat (embedding vector_cosine_ops) +WITH (lists = 200); +CREATE INDEX IF NOT EXISTS idx_book_ch ON bible_passages (book, chapter); +``` + +--- + +## 6) Retrieval patterns + +### A) Pure vector kNN (cosine) +```sql +SELECT ref, book, chapter, verse, text_raw, + 1 - (embedding <=> $1) AS cosine_sim +FROM bible_passages +ORDER BY embedding <=> $1 +LIMIT $2; +``` + +### B) Hybrid lexical + vector (weighted) +```sql +WITH v AS ( + SELECT id, 1 - (embedding <=> $1) AS vsim + FROM bible_passages + ORDER BY embedding <=> $1 + LIMIT 100 +), +l AS ( + SELECT id, ts_rank(tsv, $2) AS lrank + FROM bible_passages + WHERE tsv @@ $2 +) +SELECT bp.ref, bp.book, bp.chapter, bp.verse, bp.text_raw, + COALESCE(v.vsim, 0) * 0.7 + COALESCE(l.lrank, 0) * 0.3 AS score +FROM bible_passages bp +LEFT JOIN v ON v.id = bp.id +LEFT JOIN l ON l.id = bp.id +ORDER BY score DESC +LIMIT 20; +``` + +--- + +## 7) Chat & Prayer agent tips + +- **Answer grounding**: always cite `ref` (e.g., *Ioan 3:16*). +- **Multilingual output**: keep quotes in Romanian; explain in the user’s language. +- **Prayer agent**: constrain tone & doctrine; inject retrieved verses as anchors. + +--- + +## 8) Ops + +- Idempotent `UPSERT`. +- Backoff on 429/5xx. +- Consider keeping both `embed-3-large` and `-small` columns when migrating. + +--- + +## 9) License & attribution + +This guide references the structure of *Biblia Fidela* Markdown for ingestion demonstration. fileciteturn0file0 diff --git a/temp/bible-chat-implementation-plan.md b/temp/bible-chat-implementation-plan.md new file mode 100644 index 0000000..1725ffa --- /dev/null +++ b/temp/bible-chat-implementation-plan.md @@ -0,0 +1,1482 @@ +# Biblical Guide Web App - Granular Implementation Plan + +Primary UI Language: Romanian +Secondary UI Language: English + +## Project Overview +A self-hosted Bible study web application with AI chat capabilities, real-time features, and comprehensive user management using PostgreSQL with extensions for all functionality. + +## Technology Stack + +### Core Technologies +- **Frontend**: Next.js 14 (App Router) + TypeScript +- **Backend**: Next.js API Routes + WebSocket Server +- **Database**: PostgreSQL 16 with extensions (pgvector, pg_cron, pg_jwt) +- **Cache**: PostgreSQL with UNLOGGED tables for caching +- **Real-time**: Socket.io for WebSocket connections +- **AI/LLM**: Azure OpenAI API + Ollama API +- **Containerization**: Docker + Docker Compose +- **Testing**: Jest + React Testing Library +- **Styling**: Tailwind CSS + shadcn/ui components + +## Project Structure +``` + +├── docker/ +│ ├── Dockerfile.dev +│ ├── Dockerfile.prod +│ └── nginx/ +│ └── nginx.conf +├── docker-compose.yml +├── docker-compose.prod.yml +├── src/ +│ ├── app/ +│ │ ├── (auth)/ +│ │ ├── (main)/ +│ │ ├── api/ +│ │ └── layout.tsx +│ ├── components/ +│ ├── lib/ +│ ├── hooks/ +│ ├── types/ +│ ├── utils/ +│ └── middleware.ts +├── prisma/ +│ ├── schema.prisma +│ ├── migrations/ +│ └── seed/ +├── scripts/ +│ ├── import-bible.ts +│ └── setup-db.sh +├── tests/ +├── public/ +└── config/ +``` + +## Phase 1: Foundation Setup (Days 1-3) + +### Step 1.1: Initialize Project and Docker Environment +```bash +# Create project directory +mkdir bible-chat-app && cd bible-chat-app + +# Initialize Next.js with TypeScript +npx create-next-app@latest . --typescript --tailwind --app --no-src-dir + +# Create Docker configuration +``` + +**Docker Compose Configuration (docker-compose.yml):** +```yaml +version: '3.8' +services: + postgres: + image: pgvector/pgvector:pg16 + environment: + POSTGRES_DB: bible_chat + POSTGRES_USER: bible_admin + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - postgres_data:/var/lib/postgresql/data + - ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql + ports: + - "5432:5432" + + app: + build: + context: . + dockerfile: docker/Dockerfile.dev + volumes: + - .:/app + - /app/node_modules + ports: + - "3000:3000" + - "3001:3001" # WebSocket port + depends_on: + - postgres + environment: + DATABASE_URL: postgresql://bible_admin:${DB_PASSWORD}@postgres:5432/bible_chat + AZURE_OPENAI_KEY: ${AZURE_OPENAI_KEY} + OLLAMA_API_URL: ${OLLAMA_API_URL} +``` + +### Step 1.2: Database Schema Setup + +**Install Dependencies:** +```bash +npm install @prisma/client prisma @types/node +npm install -D @types/bcryptjs bcryptjs +``` + +**Prisma Schema (prisma/schema.prisma):** +```prisma +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model User { + id String @id @default(uuid()) + email String @unique + passwordHash String + name String? + theme String @default("light") + fontSize String @default("medium") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + lastLoginAt DateTime? + + sessions Session[] + bookmarks Bookmark[] + notes Note[] + chatMessages ChatMessage[] + prayerRequests PrayerRequest[] + readingHistory ReadingHistory[] + preferences UserPreference[] +} + +model Session { + id String @id @default(uuid()) + userId String + token String @unique + expiresAt DateTime + createdAt DateTime @default(now()) + + user User @relation(fields: [userId], references: [id]) + + @@index([userId]) + @@index([token]) +} + +model BibleBook { + id Int @id + name String + testament String + orderNum Int + chapters BibleChapter[] + + @@index([testament]) +} + +model BibleChapter { + id String @id @default(uuid()) + bookId Int + chapterNum Int + verses BibleVerse[] + + book BibleBook @relation(fields: [bookId], references: [id]) + + @@unique([bookId, chapterNum]) + @@index([bookId]) +} + +model BibleVerse { + id String @id @default(uuid()) + chapterId String + verseNum Int + text String @db.Text + version String @default("KJV") + + chapter BibleChapter @relation(fields: [chapterId], references: [id]) + bookmarks Bookmark[] + notes Note[] + + @@unique([chapterId, verseNum, version]) + @@index([chapterId]) + @@index([version]) +} + +model ChatMessage { + id String @id @default(uuid()) + userId String + role String // 'user' or 'assistant' + content String @db.Text + metadata Json? // Store verse references, etc. + createdAt DateTime @default(now()) + + user User @relation(fields: [userId], references: [id]) + + @@index([userId, createdAt]) +} + +model Bookmark { + id String @id @default(uuid()) + userId String + verseId String + note String? + color String @default("#FFD700") + createdAt DateTime @default(now()) + + user User @relation(fields: [userId], references: [id]) + verse BibleVerse @relation(fields: [verseId], references: [id]) + + @@unique([userId, verseId]) + @@index([userId]) +} + +model PrayerRequest { + id String @id @default(uuid()) + userId String? + content String @db.Text + isAnonymous Boolean @default(true) + prayerCount Int @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + user User? @relation(fields: [userId], references: [id]) + prayers Prayer[] + + @@index([createdAt]) +} + +model Prayer { + id String @id @default(uuid()) + requestId String + ipAddress String // For anonymous prayer counting + createdAt DateTime @default(now()) + + request PrayerRequest @relation(fields: [requestId], references: [id]) + + @@unique([requestId, ipAddress]) +} +``` + +### Step 1.3: Database Extensions and Functions + +**Database Initialization Script (scripts/init.sql):** +```sql +-- Enable required extensions +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +CREATE EXTENSION IF NOT EXISTS "pgcrypto"; +CREATE EXTENSION IF NOT EXISTS "pg_trgm"; +CREATE EXTENSION IF NOT EXISTS "vector"; + +-- Create cache table for Bible verses +CREATE UNLOGGED TABLE verse_cache ( + key VARCHAR(255) PRIMARY KEY, + value TEXT, + expires_at TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW() +); + +-- Create index for full-text search +CREATE INDEX verse_text_gin_idx ON "BibleVerse" USING gin(to_tsvector('english', text)); + +-- Function for verse search +CREATE OR REPLACE FUNCTION search_verses(search_query TEXT, limit_count INT DEFAULT 10) +RETURNS TABLE( + verse_id UUID, + book_name TEXT, + chapter_num INT, + verse_num INT, + verse_text TEXT, + rank REAL +) AS $$ +BEGIN + RETURN QUERY + SELECT + v.id, + b.name, + c."chapterNum", + v."verseNum", + v.text, + ts_rank(to_tsvector('english', v.text), plainto_tsquery('english', search_query)) as rank + FROM "BibleVerse" v + JOIN "BibleChapter" c ON v."chapterId" = c.id + JOIN "BibleBook" b ON c."bookId" = b.id + WHERE to_tsvector('english', v.text) @@ plainto_tsquery('english', search_query) + ORDER BY rank DESC + LIMIT limit_count; +END; +$$ LANGUAGE plpgsql; + +-- Session cleanup function +CREATE OR REPLACE FUNCTION cleanup_expired_sessions() +RETURNS void AS $$ +BEGIN + DELETE FROM "Session" WHERE "expiresAt" < NOW(); +END; +$$ LANGUAGE plpgsql; +``` + +## Phase 2: Core Backend Implementation (Days 4-8) + +### Step 2.1: Environment Configuration + +**.env.local file:** +```env +DATABASE_URL=postgresql://bible_admin:password@localhost:5432/bible_chat +NEXTAUTH_URL=http://localhost:3000 +NEXTAUTH_SECRET=generate-random-secret-here +AZURE_OPENAI_KEY=your-azure-key +AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com +AZURE_OPENAI_DEPLOYMENT=gpt-4 +OLLAMA_API_URL=http://your-ollama-server:11434 +JWT_SECRET=your-jwt-secret +REDIS_URL=redis://localhost:6379 +``` + +### Step 2.2: Database Connection and Utilities + +**src/lib/db.ts:** +```typescript +import { PrismaClient } from '@prisma/client' + +const globalForPrisma = globalThis as unknown as { + prisma: PrismaClient | undefined +} + +export const prisma = globalForPrisma.prisma ?? new PrismaClient({ + log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'], +}) + +if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma +``` + +### Step 2.3: Authentication System + +**src/lib/auth/index.ts:** +```typescript +import bcrypt from 'bcryptjs' +import jwt from 'jsonwebtoken' +import { prisma } from '@/lib/db' + +export async function createUser(email: string, password: string, name?: string) { + const passwordHash = await bcrypt.hash(password, 10) + return prisma.user.create({ + data: { email, passwordHash, name } + }) +} + +export async function validateUser(email: string, password: string) { + const user = await prisma.user.findUnique({ where: { email } }) + if (!user) return null + + const isValid = await bcrypt.compare(password, user.passwordHash) + return isValid ? user : null +} + +export function generateToken(userId: string): string { + return jwt.sign({ userId }, process.env.JWT_SECRET!, { expiresIn: '7d' }) +} +``` + +### Step 2.4: Bible Data Import Script + +**scripts/import-bible.ts:** +```typescript +import { PrismaClient } from '@prisma/client' +import fs from 'fs/promises' +import path from 'path' + +const prisma = new PrismaClient() + +interface BibleData { + books: Array<{ + id: number + name: string + testament: string + chapters: Array<{ + number: number + verses: Array<{ + number: number + text: string + }> + }> + }> +} + +async function importBible() { + const dataPath = path.join(process.cwd(), 'data', 'bible-kjv.json') + const bibleData: BibleData = JSON.parse(await fs.readFile(dataPath, 'utf-8')) + + for (const book of bibleData.books) { + // Create book + await prisma.bibleBook.create({ + data: { + id: book.id, + name: book.name, + testament: book.testament, + orderNum: book.id + } + }) + + // Create chapters and verses + for (const chapter of book.chapters) { + const createdChapter = await prisma.bibleChapter.create({ + data: { + bookId: book.id, + chapterNum: chapter.number + } + }) + + // Bulk create verses + await prisma.bibleVerse.createMany({ + data: chapter.verses.map(verse => ({ + chapterId: createdChapter.id, + verseNum: verse.number, + text: verse.text, + version: 'KJV' + })) + }) + } + } +} + +importBible() + .then(() => console.log('Bible import completed')) + .catch(console.error) + .finally(() => prisma.$disconnect()) +``` + +## Phase 3: API Routes Implementation (Days 9-12) + +### Step 3.1: Authentication API Routes + +**src/app/api/auth/register/route.ts:** +```typescript +import { NextResponse } from 'next/server' +import { createUser, generateToken } from '@/lib/auth' +import { prisma } from '@/lib/db' + +export async function POST(request: Request) { + try { + const { email, password, name } = await request.json() + + // Validation + if (!email || !password) { + return NextResponse.json({ error: 'Email and password required' }, { status: 400 }) + } + + // Check if user exists + const existing = await prisma.user.findUnique({ where: { email } }) + if (existing) { + return NextResponse.json({ error: 'User already exists' }, { status: 409 }) + } + + // Create user + const user = await createUser(email, password, name) + const token = generateToken(user.id) + + // Create session + await prisma.session.create({ + data: { + userId: user.id, + token, + expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) + } + }) + + return NextResponse.json({ user: { id: user.id, email: user.email, name: user.name }, token }) + } catch (error) { + return NextResponse.json({ error: 'Server error' }, { status: 500 }) + } +} +``` + +### Step 3.2: Bible API Routes + +**src/app/api/bible/search/route.ts:** +```typescript +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/db' + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url) + const query = searchParams.get('q') + const limit = parseInt(searchParams.get('limit') || '10') + + if (!query) { + return NextResponse.json({ error: 'Query required' }, { status: 400 }) + } + + // Use raw SQL for full-text search + const results = await prisma.$queryRaw` + SELECT * FROM search_verses(${query}, ${limit}) + ` + + return NextResponse.json({ results }) +} +``` + +### Step 3.3: AI Chat API Integration + +**src/lib/ai/azure-openai.ts:** +```typescript +import { AzureOpenAI } from 'openai' + +const client = new AzureOpenAI({ + apiKey: process.env.AZURE_OPENAI_KEY!, + apiVersion: '2024-02-01', + endpoint: process.env.AZURE_OPENAI_ENDPOINT!, +}) + +export async function generateChatResponse( + messages: Array<{ role: string; content: string }>, + verseContext?: string +) { + const systemPrompt = `You are a helpful Bible study assistant. Always provide scripture references for your answers. ${verseContext ? `Context: ${verseContext}` : ''}` + + const response = await client.chat.completions.create({ + model: process.env.AZURE_OPENAI_DEPLOYMENT!, + messages: [ + { role: 'system', content: systemPrompt }, + ...messages + ], + temperature: 0.7, + max_tokens: 1000 + }) + + return response.choices[0].message.content +} +``` + +**src/lib/ai/embeddings.ts:** +```typescript +export async function generateEmbedding(text: string): Promise { + const response = await fetch(`${process.env.OLLAMA_API_URL}/api/embeddings`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model: 'nomic-embed-text', + prompt: text + }) + }) + + const data = await response.json() + return data.embedding +} +``` + +### Step 3.4: WebSocket Server for Real-time Features + +**src/lib/websocket/server.ts:** +```typescript +import { Server } from 'socket.io' +import { createServer } from 'http' +import { parse } from 'url' +import next from 'next' + +const dev = process.env.NODE_ENV !== 'production' +const app = next({ dev }) +const handle = app.getRequestHandler() + +app.prepare().then(() => { + const server = createServer((req, res) => { + const parsedUrl = parse(req.url!, true) + handle(req, res, parsedUrl) + }) + + const io = new Server(server, { + cors: { + origin: process.env.NEXTAUTH_URL, + methods: ['GET', 'POST'] + } + }) + + io.on('connection', (socket) => { + console.log('Client connected:', socket.id) + + // Join prayer room + socket.on('join-prayer-room', () => { + socket.join('prayers') + }) + + // Handle new prayer + socket.on('new-prayer', async (data) => { + // Save to database + // Broadcast to all in prayer room + io.to('prayers').emit('prayer-added', data) + }) + + // Handle prayer count update + socket.on('pray-for', async (requestId) => { + // Update count in database + // Broadcast updated count + io.to('prayers').emit('prayer-count-updated', { requestId, count: newCount }) + }) + + socket.on('disconnect', () => { + console.log('Client disconnected:', socket.id) + }) + }) + + server.listen(3001, () => { + console.log('WebSocket server running on port 3001') + }) +}) +``` + +## Phase 4: Frontend Implementation (Days 13-18) + +Primary UI Language: Romanian +Secondary UI Language: English + +### Step 4.1: Component Library Setup + +**Install UI Dependencies:** +```bash +npm install @radix-ui/react-dropdown-menu @radix-ui/react-dialog +npm install @radix-ui/react-tabs @radix-ui/react-toast +npm install socket.io-client zustand +npm install react-markdown remark-gfm +``` + +### Step 4.2: Global State Management + +**src/lib/store/index.ts:** +```typescript +import { create } from 'zustand' +import { persist } from 'zustand/middleware' + +interface AppState { + user: User | null + theme: 'light' | 'dark' + fontSize: 'small' | 'medium' | 'large' + currentBook: number + currentChapter: number + bookmarks: Bookmark[] + setUser: (user: User | null) => void + setTheme: (theme: 'light' | 'dark') => void + setFontSize: (size: 'small' | 'medium' | 'large') => void +} + +export const useStore = create()( + persist( + (set) => ({ + user: null, + theme: 'light', + fontSize: 'medium', + currentBook: 1, + currentChapter: 1, + bookmarks: [], + setUser: (user) => set({ user }), + setTheme: (theme) => set({ theme }), + setFontSize: (fontSize) => set({ fontSize }), + }), + { + name: 'bible-chat-storage', + } + ) +) +``` + +### Step 4.3: Main Layout Component + +**src/app/layout.tsx:** +```typescript +import './globals.css' +import { Providers } from '@/components/providers' +import { Navigation } from '@/components/navigation' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + + +
+ {children} +
+
+ + + ) +} +``` + +### Step 4.4: Bible Reader Component + +**src/components/bible/reader.tsx:** +```typescript +'use client' + +import { useState, useEffect } from 'react' +import { useStore } from '@/lib/store' + +export function BibleReader() { + const { currentBook, currentChapter } = useStore() + const [verses, setVerses] = useState([]) + const [loading, setLoading] = useState(true) + + useEffect(() => { + fetchChapter(currentBook, currentChapter) + }, [currentBook, currentChapter]) + + async function fetchChapter(bookId: number, chapterNum: number) { + setLoading(true) + const res = await fetch(`/api/bible/chapter?book=${bookId}&chapter=${chapterNum}`) + const data = await res.json() + setVerses(data.verses) + setLoading(false) + } + + const handleVerseClick = async (verseId: string) => { + // Add to bookmarks + await fetch('/api/bookmarks', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ verseId }) + }) + } + + if (loading) return
Loading...
+ + return ( +
+ {verses.map((verse: any) => ( + handleVerseClick(verse.id)} + > + {verse.verseNum} + {verse.text} + + ))} +
+ ) +} +``` + +### Step 4.5: AI Chat Interface + +**src/components/chat/chat-interface.tsx:** +```typescript +'use client' + +import { useState, useRef, useEffect } from 'react' +import { Send } from 'lucide-react' + +export function ChatInterface() { + const [messages, setMessages] = useState([]) + const [input, setInput] = useState('') + const [loading, setLoading] = useState(false) + const messagesEndRef = useRef(null) + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }) + } + + useEffect(scrollToBottom, [messages]) + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + if (!input.trim() || loading) return + + const userMessage = { role: 'user', content: input } + setMessages(prev => [...prev, userMessage]) + setInput('') + setLoading(true) + + try { + const res = await fetch('/api/chat', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + messages: [...messages, userMessage] + }) + }) + + const data = await res.json() + setMessages(prev => [...prev, { + role: 'assistant', + content: data.response + }]) + } catch (error) { + console.error('Chat error:', error) + } finally { + setLoading(false) + } + } + + return ( +
+
+ {messages.map((msg, idx) => ( +
+
+ {msg.content} +
+
+ ))} + {loading && ( +
+
+
+
+
+
+
+
+
+ )} +
+
+ +
+
+ setInput(e.target.value)} + placeholder="Ask about the Bible..." + className="flex-1 px-4 py-2 border rounded-lg focus:outline-none focus:ring-2" + disabled={loading} + /> + +
+
+
+ ) +} +``` + +### Step 4.6: Real-time Prayer Wall + +**src/components/prayer/prayer-wall.tsx:** +```typescript +'use client' + +import { useEffect, useState } from 'react' +import { io, Socket } from 'socket.io-client' + +export function PrayerWall() { + const [socket, setSocket] = useState(null) + const [prayers, setPrayers] = useState([]) + const [newPrayer, setNewPrayer] = useState('') + + useEffect(() => { + const socketInstance = io('http://localhost:3001') + setSocket(socketInstance) + + socketInstance.emit('join-prayer-room') + + socketInstance.on('prayer-added', (prayer) => { + setPrayers(prev => [prayer, ...prev]) + }) + + socketInstance.on('prayer-count-updated', ({ requestId, count }) => { + setPrayers(prev => prev.map(p => + p.id === requestId ? { ...p, prayerCount: count } : p + )) + }) + + // Load initial prayers + fetchPrayers() + + return () => { + socketInstance.disconnect() + } + }, []) + + const fetchPrayers = async () => { + const res = await fetch('/api/prayers') + const data = await res.json() + setPrayers(data.prayers) + } + + const handleSubmitPrayer = async (e: React.FormEvent) => { + e.preventDefault() + if (!newPrayer.trim()) return + + const res = await fetch('/api/prayers', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ content: newPrayer, isAnonymous: true }) + }) + + const prayer = await res.json() + socket?.emit('new-prayer', prayer) + setNewPrayer('') + } + + const handlePrayFor = (requestId: string) => { + socket?.emit('pray-for', requestId) + } + + return ( +
+
+