Navigation
Famous.AI Integration - Fixed Code

Copy-paste ready components with all bugs fixed ✅

src/components/PendingPearlsNotification.tsx
import React, { useState, useEffect, useRef } from 'react';
import { getStoredPlatformAuth } from '../utils/platformAuth';

export default function PendingPearlsNotification() {
  const [pendingCount, setPendingCount] = useState(0);
  const [syncing, setSyncing] = useState(false);
  const [syncProgress, setSyncProgress] = useState(0);
  const retryCountRef = useRef(0);
  const [lastError, setLastError] = useState('');

  useEffect(() => {
    const checkPending = () => {
      const pending = JSON.parse(localStorage.getItem('pending_pearl_awards') || '[]');
      setPendingCount(pending.length);
    };

    checkPending();
    const interval = setInterval(checkPending, 10000);
    return () => clearInterval(interval);
  }, []);

  const syncPendingPearlAwards = async () => {
    setSyncing(true);
    setSyncProgress(0);
    setLastError('');
    
    const pending = JSON.parse(localStorage.getItem('pending_pearl_awards') || '[]');

    // ✅ FIX: Use platformAuth module instead of raw sessionStorage
    const authData = getStoredPlatformAuth();
    const token = authData?.token;

    if (!token) {
      alert('Please reconnect to Snappy Idioms platform');
      setSyncing(false);
      return;
    }

    const synced = [];
    for (let i = 0; i < pending.length; i++) {
      const award = pending[i];
      try {
        // ✅ Exponential backoff with cap at 30s
        const backoffDelay = Math.min(Math.pow(2, retryCountRef.current) * 1000, 30000);
        if (retryCountRef.current > 0) {
          await new Promise(r => setTimeout(r, backoffDelay));
        }

        const response = await fetch(`https://snappyidioms.com/api/platform-api`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token}`
          },
          body: JSON.stringify({
            action: 'award_pearls',
            pearls: award.pearls,
            reason: award.reason,
            levelId: award.levelId
          })
        });

        if (response.ok) {
          synced.push(award);
          retryCountRef.current = 0;
          setSyncProgress(Math.floor(((i + 1) / pending.length) * 100));
        } else {
          retryCountRef.current += 1;
          const nextDelay = Math.min(Math.pow(2, retryCountRef.current) * 1000, 30000);
          setLastError(`Failed, retrying in ${nextDelay/1000}s...`);
        }
      } catch (error) {
        console.error('Sync failed:', error);
        retryCountRef.current += 1;
        const nextDelay = Math.min(Math.pow(2, retryCountRef.current) * 1000, 30000);
        setLastError(`Network error, retrying in ${nextDelay/1000}s...`);
      }
    }

    const remaining = pending.filter(p => !synced.includes(p));
    localStorage.setItem('pending_pearl_awards', JSON.stringify(remaining));
    setPendingCount(remaining.length);
    setSyncing(false);
    setSyncProgress(0);
  };

  if (pendingCount === 0) return null;

  return (
    <div className="fixed bottom-4 right-4 bg-gradient-to-r from-amber-400 to-orange-500 text-white p-4 rounded-lg shadow-xl max-w-sm">
      <div className="flex items-center justify-between mb-2">
        <span className="font-bold">⚠️ Pending Pearls: {pendingCount}</span>
        <button
          onClick={syncPendingPearlAwards}
          disabled={syncing}
          className="bg-white text-orange-600 px-3 py-1 rounded-lg font-semibold hover:bg-orange-50 disabled:opacity-50"
        >
          {syncing ? 'Syncing...' : retryCountRef.current > 0 ? `Retry (${retryCountRef.current})` : 'Sync Now'}
        </button>
      </div>
      
      {syncing && (
        <div className="space-y-1">
          <div className="w-full bg-white/30 rounded-full h-2 overflow-hidden">
            <div 
              className="h-full bg-white transition-all duration-300" 
              style={{ width: `${syncProgress}%` }} 
            />
          </div>
          <div className="text-xs text-white/90 text-center">{syncProgress}% synced</div>
        </div>
      )}
      
      {lastError && !syncing && (
        <div className="text-xs mt-2 text-white/90">{lastError}</div>
      )}
    </div>
  );
}
✅ Fixes Applied:
  • • Uses platformAuth module instead of raw sessionStorage
  • • Exponential backoff capped at 30s (2s → 4s → 8s → 16s → 30s)
  • • Visual progress bar with percentage indicator
  • • Retry attempt counter in button text
  • • Error feedback showing next backoff delay
Installation Steps
1
Create Component Files
Copy each component into your Famous.AI src/ folder
2
Add to App.tsx Routes
Import PlatformStats and add route: <Route path="/platform-stats" element=<PlatformStats /> />
3
Update Navigation
Add "Platform Stats" link to your main menu
4
Test Integration
Play a round → Check pending pearls → Wait for token refresh → View stats