Skip to content

Implement live AI-generated content and real-time stats on home route#3

Draft
Copilot wants to merge 6 commits intomainfrom
copilot/update-home-route-data-fetching
Draft

Implement live AI-generated content and real-time stats on home route#3
Copilot wants to merge 6 commits intomainfrom
copilot/update-home-route-data-fetching

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 21, 2026

Replaces static placeholder content with AI-generated hero content and live product statistics using the existing Gemini service infrastructure.

Changes

Gemini Service Enhancement

  • Added fetchHomeContent() method returning structured JSON for hero section and features
  • Implements robust JSON parsing with multiple fallback strategies for various AI response formats
  • Includes runtime validation with type guards to ensure response structure integrity
  • Comprehensive error logging for production debugging
  • Fallback to default content on API failure or validation errors
async fetchHomeContent(): Promise<HomeContent> {
  const response = await this.ai.models.generateContent({
    model: MODELS.FAST_FLASH,
    contents: prompt,
    config: { systemInstruction: `...` }
  });
  
  // Multi-strategy JSON parsing
  let jsonText = response.text.trim();
  jsonText = jsonText.replace(/```json\n?/g, '').replace(/```\n?/g, '');
  const jsonMatch = jsonText.match(/\{[\s\S]*\}/);
  if (jsonMatch) jsonText = jsonMatch[0];
  
  const parsed = JSON.parse(jsonText);
  
  // Validate structure before use
  if (!this.isValidHomeContent(parsed)) {
    return this.getFallbackContent();
  }
  
  return parsed;
}

Caching Implementation

  • 24-hour localStorage cache to reduce API calls by ~99%
  • Automatic cache validation and invalidation
  • Graceful fallback on cache errors
  • Significantly reduces API costs in production

Live Statistics

  • LiveStatsBar component displays 6 metrics (3 dynamic, 3 static)
  • Dynamic counts: total products, in-stock items, categories
  • Auto-recalculates on product state changes via useEffect
  • Uses local product calculations for accuracy, not AI-generated stats

Home View Updates

  • Hero section conditionally renders AI content or falls back to slide data
  • Dynamic features section maps AI-generated cards with icons
  • Fallback Shield icon for unknown icon names to maintain UI consistency
  • Preserves existing WhyNexlyn and ResellerProgram components

Type Definitions

  • Added HomeContent interface for structured AI response data (hero and features only)
  • Removed unused stats field for cleaner API responses

Configuration

Required environment variable in .env.local:

API_KEY=your_gemini_api_key_here

Optional AI Studio data source integration available via DATA_SOURCE_ID env var and dataSourceGrounding config. Current implementation uses general content generation with Google Search grounding fallback.

Production Features

  • ✅ Robust error handling with fallback content
  • ✅ Runtime type validation
  • ✅ 24-hour caching mechanism
  • ✅ Comprehensive error logging
  • ✅ Consistent UI with fallback icons
  • ✅ Backward compatible
Original prompt

Objective

Replace placeholder content on the home route with actual data fetched from the AI Studio data source using the existing Gemini service. Create live components that display real, grounded information from your data source.


Current Architecture Understanding

Existing Gemini Integration

  • Service: services/geminiService.ts
  • Model: gemini-2.0-flash-exp
  • Current capability: Uses Google Search grounding via tools: [{ googleSearch: {} }]
  • Data extraction: Already extracting groundingMetadata.groundingChunks for sources

Current Home View

  • Located in App.tsx around lines 840-947
  • Shows hero slides with images
  • Has animated transitions
  • Currently uses hardcoded HERO_SLIDES from constants

Step-by-Step Implementation Guide

Step 1: Create New Data Fetching Service

File: services/geminiService.ts

Add new method to fetch specific content from your AI Studio data source:

// Add this method to the GeminiService class
async fetchHomeContent(): Promise<{
  hero: {
    title: string;
    subtitle: string;
    description: string;
  };
  stats: {
    label: string;
    value: string;
    icon: string;
  }[];
  features: {
    title: string;
    description: string;
    icon: string;
  }[];
}> {
  const prompt = `
    Provide structured information about NEXLYN Distribution LLC:
    1. Hero section with compelling title, subtitle, and description about being a MikroTik Master Distributor
    2. Three key statistics (e.g., years in business, product categories, regional coverage)
    3. Four main features/benefits of partnering with NEXLYN
    
    Format as JSON with clear structure.
  `;

  const response = await this.ai.models.generateContent({
    model: MODELS.FAST_FLASH,
    contents: prompt,
    config: {
      systemInstruction: `You are a content expert for NEXLYN Distributions. 
      Provide accurate, professional B2B content about MikroTik distribution in the Middle East and Africa.
      Return only valid JSON without markdown formatting.`,
    },
  });

  try {
    const jsonText = response.text.replace(/```json\n?|\n?```/g, '').trim();
    return JSON.parse(jsonText);
  } catch (err) {
    // Fallback with default structure
    return {
      hero: {
        title: "NEXLYN DISTRIBUTIONS",
        subtitle: "Official MikroTik® Master Distributor",
        description: "Serving Middle East & Africa with carrier-grade networking solutions"
      },
      stats: [
        { label: "Product Categories", value: "6+", icon: "Router" },
        { label: "Regional Coverage", value: "MEA", icon: "Globe" },
        { label: "Stock Status", value: "Live", icon: "Shield" }
      ],
      features: [
        { title: "Master Distributor", description: "Official MikroTik partnership", icon: "Shield" },
        { title: "Carrier-Grade", description: "Enterprise networking hardware", icon: "Router" },
        { title: "Regional Hub", description: "Dubai-based distribution center", icon: "Globe" },
        { title: "Technical Support", description: "Expert deployment guidance", icon: "Shield" }
      ]
    };
  }
}

Step 2: Create Live Product Stats Component

File: App.tsx

Add state for live data:

// Add to App component state (around line 44)
const [homeContent, setHomeContent] = useState<any>(null);
const [liveStats, setLiveStats] = useState({
  totalProducts: 0,
  inStock: 0,
  categories: 0
});

Add effect to fetch live data on mount:

// Add useEffect (around line 100)
useEffect(() => {
  const fetchHomeData = async () => {
    try {
      const content = await gemini.fetchHomeContent();
      setHomeContent(content);
    } catch (err) {
      console.error('Failed to fetch home content:', err);
    }
  };
  
  fetchHomeData();
}, []);

// Update live stats whenever products change
useEffect(() => {
  setLiveStats({
    totalProducts: products.length,
    inStock: products.filter(p => p.status === 'In Stock').length,
    categories: new Set(products.map(p => p.category)).size
  });
}, [products]);

Step 3: Create Live Stats Display Component

File: App.tsx

Add this component (around line 240, before the Logo component):

const LiveStatsBar = () => (
  <div className="bg-nexlyn/5 border-y border-nexlyn/10 py-6 overflow-hidden">
    <div className="max-w-7xl mx-auto px-6">
      <div className="grid grid-cols-3 md:grid-cols-6 gap-8 items-center">
        <div className="text-center space-y-2">
          <div className="text-3xl font-black text-nexlyn">{liveStats.totalProducts}</div>
          <div className="text-[8px] font-bold uppercase tracking-widest text-slate-500">Products</div>
        </div>
        <div className="text-center space-y-2">
          <div className="text-3xl font-black text-green-500">{liveStats.inStock}</div>
          <div className="text-[8px] font-bold uppercase tracking-widest text-slate-500">In...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

@vercel
Copy link
Copy Markdown

vercel Bot commented Jan 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
nexlyn Ready Ready Preview, Comment Jan 21, 2026 3:11pm
nexlyn-v2 Error Error Jan 21, 2026 3:11pm
nexlyn-v2-1 Ready Ready Preview, Comment Jan 21, 2026 3:11pm

Co-authored-by: vishnu-madhavan-git <237662584+vishnu-madhavan-git@users.noreply.github.com>
Co-authored-by: vishnu-madhavan-git <237662584+vishnu-madhavan-git@users.noreply.github.com>
Co-authored-by: vishnu-madhavan-git <237662584+vishnu-madhavan-git@users.noreply.github.com>
Copilot AI changed the title [WIP] Replace placeholder content with actual data from AI Studio Implement live AI-generated content and real-time stats on home route Jan 21, 2026
@vishnu-madhavan-git vishnu-madhavan-git requested review from Copilot and removed request for vishnu-madhavan-git January 21, 2026 11:40
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements AI-generated content and live statistics on the home route, replacing static placeholder content with dynamic data fetched from Google's Gemini API. The implementation adds a new service method, UI components, and state management to display real-time product metrics and AI-generated hero content.

Changes:

  • Added fetchHomeContent() method to Gemini service for AI-generated hero section and features
  • Implemented LiveStatsBar component displaying 6 metrics (3 dynamic product-based, 3 static)
  • Enhanced home view with conditional rendering for AI content with fallback to static slides

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 11 comments.

File Description
types.ts Adds HomeContent interface for structured AI response data with hero, stats, and features fields
services/geminiService.ts Implements fetchHomeContent() method with JSON parsing and fallback error handling
App.tsx Adds state management for home content and live stats, new LiveStatsBar component, and conditional rendering in hero section
LIVE_DATA_IMPLEMENTATION.md Comprehensive documentation covering implementation details, configuration, and troubleshooting

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread services/geminiService.ts Outdated
Comment on lines +85 to +86
const jsonText = response.text.replace(/```json\n?|\n?```/g, '').trim();
return JSON.parse(jsonText);
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSON parsing approach is fragile and may fail for valid JSON responses. The regex pattern /```json\n?|\n?```/g only handles markdown code blocks with 'json' syntax highlighting, but the AI might return JSON without markdown formatting or with different formatting. Consider a more robust approach that handles various response formats, or better yet, configure the AI to use structured output mode if available in the API.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improved JSON parsing to handle various response formats. Now tries multiple strategies: removes markdown code blocks with different patterns, extracts JSON using regex, and validates structure before use. Commit 29fabcd

Comment thread App.tsx Outdated
return (
<div key={idx} className="glass-panel p-8 rounded-2xl border border-black/5 dark:border-white/5 hover:border-nexlyn/30 transition-all space-y-4">
<div className="w-12 h-12 rounded-xl bg-nexlyn/10 flex items-center justify-center">
{IconComponent ? <IconComponent className="w-6 h-6 text-nexlyn" /> : null}
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The icon mapping could fail silently if the AI returns an icon name that doesn't exist in the ICONS object. When ICONS[feature.icon as keyof typeof ICONS] is undefined, the component renders nothing in the icon container. Consider adding a fallback icon or validation to ensure a consistent UI even when the AI returns unexpected icon names.

Suggested change
{IconComponent ? <IconComponent className="w-6 h-6 text-nexlyn" /> : null}
{IconComponent ? (
<IconComponent className="w-6 h-6 text-nexlyn" />
) : (
<span className="w-6 h-6 flex items-center justify-center text-nexlyn text-lg font-bold">
?
</span>
)}

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added Shield icon as fallback when AI returns unknown icon names. This ensures a consistent UI even with unexpected icon values. Commit 29fabcd

Comment thread App.tsx Outdated
const content = await gemini.fetchHomeContent();
setHomeContent(content);
} catch (err) {
console.error('Failed to fetch AI-generated home content, using fallback:', err);
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message in the console log references 'AI-generated home content' but the actual error from the API call is more specific. The message should be updated to reflect that it's specifically a Gemini API failure for consistency with other error messages in the codebase.

Suggested change
console.error('Failed to fetch AI-generated home content, using fallback:', err);
console.error('Failed to fetch home content from Gemini API, using fallback:', err);

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated error message to specifically reference "Gemini API" for consistency with other error messages. Commit 29fabcd

Comment thread LIVE_DATA_IMPLEMENTATION.md Outdated

Required in `.env.local`:
```bash
GEMINI_API_KEY=your_api_key_here
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation references environment variable 'GEMINI_API_KEY' but the actual code uses 'API_KEY' (line 42 of geminiService.ts). This inconsistency could confuse developers trying to configure the application. The documentation should match the actual implementation or vice versa.

Suggested change
GEMINI_API_KEY=your_api_key_here
API_KEY=your_api_key_here

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed documentation to use API_KEY instead of GEMINI_API_KEY to match the actual implementation in vite.config.ts. Added clarifying note. Commit 29fabcd

Comment thread types.ts Outdated
Comment on lines +52 to +56
stats: {
label: string;
value: string;
icon: string;
}[];
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'stats' field in the HomeContent interface is defined but never used in the UI rendering. The LiveStatsBar component uses the local liveStats state instead. Either remove the unused 'stats' field from the interface and API response, or utilize the AI-generated stats in the UI to avoid requesting unnecessary data from the API.

Suggested change
stats: {
label: string;
value: string;
icon: string;
}[];

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed unused stats field from HomeContent interface. The LiveStatsBar uses local product calculations instead of AI-generated stats. Commit 29fabcd

Comment thread App.tsx Outdated
Comment on lines +106 to +118
// Fetch home content on mount
useEffect(() => {
const fetchHomeData = async () => {
try {
const content = await gemini.fetchHomeContent();
setHomeContent(content);
} catch (err) {
console.error('Failed to fetch AI-generated home content, using fallback:', err);
}
};

fetchHomeData();
}, []);
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AI-generated content is fetched on every page load without any caching mechanism. This results in unnecessary API calls and costs. Consider implementing a caching strategy (e.g., localStorage with TTL, or state management with a refresh mechanism) to avoid redundant API calls when the user navigates between pages or refreshes.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented localStorage caching with 24-hour TTL. AI content is now cached and only refetched when cache expires or is invalid, preventing unnecessary API calls. Commit 29fabcd

Comment thread services/geminiService.ts Outdated
Comment on lines +85 to +86
const jsonText = response.text.replace(/```json\n?|\n?```/g, '').trim();
return JSON.parse(jsonText);
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSON parsing doesn't validate the structure of the response before using it. If the AI returns a valid JSON object that doesn't match the expected HomeContent interface (missing fields, wrong types, etc.), the UI will fail silently or crash. Consider adding runtime validation or type guards to ensure the response matches the expected structure before setting it in state.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added isValidHomeContent() type guard that validates the AI response structure before using it. Falls back to default content if validation fails. Commit 29fabcd

Comment thread services/geminiService.ts
Comment thread services/geminiService.ts Outdated
try {
const response = await this.ai.models.generateContent({
model: MODELS.FAST_FLASH,
contents: [{ role: 'user', parts: [{ text: prompt }] }],
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API call format is inconsistent with the existing searchTech method. The searchTech method uses contents: prompt (line 49) while fetchHomeContent uses contents: [{ role: 'user', parts: [{ text: prompt }] }]. For consistency and to avoid potential API compatibility issues, both methods should use the same format for the contents parameter.

Suggested change
contents: [{ role: 'user', parts: [{ text: prompt }] }],
contents: prompt,

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to use contents: prompt format for consistency with the existing searchTech method. Commit 29fabcd

Comment thread App.tsx Outdated
import React, { useState, useEffect, useMemo } from 'react';
import { ICONS, PRODUCTS as INITIAL_PRODUCTS, CATEGORIES, WHATSAPP_NUMBER as INITIAL_WA, ADMIN_PASSCODE, OWNER_PASSCODE, HERO_SLIDES, VISUALS } from './constants';
import { Product, Message, GroundingSource, Category } from './types';
import { Product, Message, GroundingSource, Category, HomeContent } from './types';
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused import GroundingSource.

Suggested change
import { Product, Message, GroundingSource, Category, HomeContent } from './types';
import { Product, Message, Category, HomeContent } from './types';

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed unused GroundingSource import. Commit 29fabcd

@vishnu-madhavan-git
Copy link
Copy Markdown
Collaborator

@copilot give me the git code link to share the repo

…ng, and fix docs

Co-authored-by: vishnu-madhavan-git <237662584+vishnu-madhavan-git@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Jan 21, 2026

@copilot give me the git code link to share the repo

The repository URL is: https://github.com/vishnu-madhavan-git/NEXLYN---v2

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants