import { useEffect, useMemo, useState } from 'react';
import { useAI, share } from '@deplixo/sdk';
import { PetGrid } from './components/PetGrid.jsx';
import { Favorites } from './components/Favorites.jsx';
import { AddPetForm } from './components/AddPetForm.jsx';
function App() {
const { generate, loading: aiLoading, error: aiError } = useAI();
const [activeTab, setActiveTab] = useState('browse');
const [showAddForm, setShowAddForm] = useState(false);
const [lifestyle, setLifestyle] = useState('');
const [livingSituation, setLivingSituation] = useState('');
const [recommendedPets, setRecommendedPets] = useState([]);
const [matchSummary, setMatchSummary] = useState('');
const [matchError, setMatchError] = useState('');
const [shareMessage, setShareMessage] = useState('');
const [shareError, setShareError] = useState('');
const [selectedPetId, setSelectedPetId] = useState('');
const tabs = [
{ id: 'browse', label: '🐾 Browse Pets', icon: '🏠' },
{ id: 'favorites', label: '💕 Favorites', icon: '💕' },
{ id: 'manage', label: '🏥 Manage Listings', icon: '🏥' }
];
const getPetProfileUrl = petId => {
const url = new URL(window.location.href);
url.searchParams.set('pet', petId);
return url.toString();
};
const copyShareLink = async pet => {
setShareMessage('');
setShareError('');
const shareUrl = getPetProfileUrl(pet.id);
const shareText = `Check out ${pet.name} on PawFinder: ${shareUrl}`;
try {
if (navigator.share) {
await navigator.share({
title: `${pet.name} | PawFinder`,
text: shareText,
url: shareUrl
});
setShareMessage('Share sheet opened for this pet profile.');
return;
}
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(shareUrl);
setShareMessage('Share link copied to clipboard.');
return;
}
setShareError('Sharing is not available in this browser.');
} catch (error) {
setShareError('Could not copy or share the link right now.');
}
};
const handleOpenSharedPet = async petId => {
setSelectedPetId(petId);
setActiveTab('browse');
window.history.replaceState({}, '', getPetProfileUrl(petId));
try {
const element = document.getElementById(`pet-card-${petId}`);
if (element) {
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
} catch {
// no-op
}
};
useEffect(() => {
const syncFromUrl = () => {
const url = new URL(window.location.href);
const petId = url.searchParams.get('pet') || '';
if (petId) {
setSelectedPetId(petId);
setActiveTab('browse');
}
};
syncFromUrl();
window.addEventListener('popstate', syncFromUrl);
return () => window.removeEventListener('popstate', syncFromUrl);
}, []);
const handleMatchPets = async () => {
setMatchError('');
setMatchSummary('');
try {
const response = await fetch('/api/pets');
const allPets = await response.json();
const promptPets = allPets.map((pet, index) => ({
index,
id: pet.id,
name: pet.name,
species: pet.species,
breed: pet.breed,
age: pet.age,
temperament: pet.temperament,
description: pet.description,
adoptionFee: pet.adoptionFee,
size: pet.size,
energyLevel: pet.energyLevel,
goodWithKids: pet.goodWithKids,
goodWithPets: pet.goodWithPets,
apartmentFriendly: pet.apartmentFriendly
}));
const result = await generate({
system:
'You are an adoption matchmaking assistant. Score pets from 0-100 based on how well they fit the adopter lifestyle and living situation. Return JSON only with this shape: {"summary":"string","recommendations":[{"id":"pet-id","score":0,"reason":"string"}]}. Only recommend pets from the provided list. Prefer concise reasons and sort recommendations highest score first.',
user: JSON.stringify({
adopterProfile: {
lifestyle,
livingSituation
},
pets: promptPets
}),
json: true
});
const recommendations = Array.isArray(result?.recommendations) ? result.recommendations : [];
const sortedIds = recommendations
.filter(item => item && item.id)
.sort((a, b) => (b.score ?? 0) - (a.score ?? 0))
.map(item => item.id);
const petsById = new Map(allPets.map(pet => [pet.id, pet]));
const matched = sortedIds
.map(id => petsById.get(id))
.filter(Boolean);
setRecommendedPets(matched);
setMatchSummary(result?.summary || 'Here are pets that look like a good fit based on your preferences.');
} catch (error) {
setMatchError('Unable to load pets for matching right now.');
}
};
const profileBanner = useMemo(() => {
if (!selectedPetId) return null;
return `Open pet profile: ${selectedPetId}`;
}, [selectedPetId]);
return (
🐾 PawFinder
Find your forever friend
{activeTab === 'browse' && (
AI Matchmaker
Tell us about your lifestyle and living situation, and we will recommend pets that fit best.
{aiError && {aiError}
}
{matchError && {matchError}
}
{matchSummary && {matchSummary}
}
{shareMessage &&
{shareMessage}
}
{shareError &&
{shareError}
}
{profileBanner &&
{profileBanner}
}
0}
selectedPetId={selectedPetId}
onSharePet={copyShareLink}
onOpenPet={handleOpenSharedPet}
/>
)}
{activeTab === 'favorites' && }
{activeTab === 'manage' && (
Your Pet Listings
{showAddForm &&
setShowAddForm(false)} />}
)}
);
}
// PROGRESS:sc_001:complete:Setting up the pet adoption board
ReactDOM.createRoot(document.getElementById("root")).render();