Snapshot: full project state

This commit is contained in:
2026-10-06 23:43:43 -07:00
commit fc6d00988c
13 changed files with 1283 additions and 0 deletions

197
pettalk-App-new.tsx Normal file
View File

@@ -0,0 +1,197 @@
import React, { useState } from 'react';
import { Pet, AnalysisResult, PetTimelineItem, DictionarySignal, TalkBackLog } from './types';
import { INITIAL_PET_LUNA, INITIAL_PET_MILO, INITIAL_TIMELINE_LUNA, INITIAL_DICTIONARY_LUNA } from './data/seedData';
import { Navigation } from './components/Navigation';
import { LandingPage } from './components/LandingPage';
import { OnboardingFlow } from './components/OnboardingFlow';
import { LiveListenScreen } from './components/LiveListenScreen';
import { TalkBackScreen } from './components/TalkBackScreen';
import { PetTimeline } from './components/PetTimeline';
import { InsightsScreen } from './components/InsightsScreen';
import { AuthPage } from './components/AuthPage';
import { useAuth } from './context/AuthContext';
export default function App() {
const { user, isLoading, logout } = useAuth();
const [pets, setPets] = useState<Pet[]>([INITIAL_PET_LUNA, INITIAL_PET_MILO]);
const [selectedPetId, setSelectedPetId] = useState<string>(INITIAL_PET_LUNA.id);
const [currentView, setCurrentView] = useState<'landing' | 'onboarding' | 'listen' | 'talkback' | 'timeline' | 'insights'>('landing');
const [timelineItems, setTimelineItems] = useState<PetTimelineItem[]>(INITIAL_TIMELINE_LUNA);
const [dictionarySignals, setDictionarySignals] = useState<DictionarySignal[]>(INITIAL_DICTIONARY_LUNA);
const selectedPet = pets.find(p => p.id === selectedPetId) || pets[0];
// Show loading spinner while checking auth
if (isLoading) {
return (
<div className="min-h-screen bg-[#F9F7F2] flex items-center justify-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[#F08C7D]"></div>
</div>
);
}
// Show auth page if not authenticated
if (!user) {
return <AuthPage />;
}
// Callback when analysis completes from LiveListenScreen
const handleAnalysisComplete = (result: AnalysisResult) => {
const newTimelineItem: PetTimelineItem = {
id: `tl_${Date.now()}`,
petId: result.petId,
type: 'interpretation',
title: result.primaryIntent.title,
description: result.reasoning.summary,
timestamp: result.timestamp,
intentCategory: result.primaryIntent.category,
confidence: result.primaryIntent.confidence,
feedbackStatus: 'pending',
zoneName: result.zoneName
};
setTimelineItems(prev => [newTimelineItem, ...prev]);
};
// Callback when pet model updates from training
const handleUpdatePet = (updatedPet: Pet) => {
setPets(prev => prev.map(p => p.id === updatedPet.id ? updatedPet : p));
};
// Callback when Talk Back reaction is logged
const handleLogTalkBackReaction = (log: TalkBackLog) => {
const newTimelineItem: PetTimelineItem = {
id: `tl_tb_${Date.now()}`,
petId: log.petId,
type: 'talk_back',
title: `Played "${log.messageLabel}" Tone`,
description: `Pet reaction observed: ${log.petReaction ? log.petReaction.replace('_', ' ') : 'played'}`,
timestamp: log.timestamp
};
setTimelineItems(prev => [newTimelineItem, ...prev]);
};
// Callback when manual event is added
const handleAddManualEvent = (event: PetTimelineItem) => {
setTimelineItems(prev => [event, ...prev]);
};
// Callback when onboarding completes
const handleCompleteOnboarding = (newPet: Pet) => {
setPets(prev => [...prev, newPet]);
setSelectedPetId(newPet.id);
setCurrentView('listen');
};
// Callback to reset pet ML model
const handleResetPetModel = () => {
setPets(prev => prev.map(p => {
if (p.id === selectedPetId) {
return { ...p, confirmedSampleCount: 0, overallAccuracy: 75 };
}
return p;
}));
alert(`${selectedPet.name}'s profile memory has been reset.`);
};
// Callback to delete pet account
const handleDeletePetAccount = () => {
const remainingPets = pets.filter(p => p.id !== selectedPetId);
if (remainingPets.length > 0) {
setPets(remainingPets);
setSelectedPetId(remainingPets[0].id);
setCurrentView('landing');
} else {
// Re-seed Luna if all pets deleted
setPets([INITIAL_PET_LUNA]);
setSelectedPetId(INITIAL_PET_LUNA.id);
setCurrentView('landing');
}
};
return (
<div className="min-h-screen bg-[#F9F7F2] text-[#1A2A40] font-sans antialiased selection:bg-[#F08C7D]/30 selection:text-[#1A2A40]">
{/* Global Responsive Navigation Header & Bottom Bar */}
<Navigation
currentView={currentView}
setCurrentView={setCurrentView}
pets={pets}
selectedPet={selectedPet}
setSelectedPetId={setSelectedPetId}
onOpenAddPet={() => setCurrentView('onboarding')}
/>
{/* Main Content View Switcher */}
<main className="max-w-5xl mx-auto px-4 py-6 sm:py-8">
{currentView === 'landing' && (
<LandingPage
selectedPet={selectedPet}
onStartListen={() => setCurrentView('listen')}
onStartOnboarding={() => setCurrentView('onboarding')}
/>
)}
{currentView === 'onboarding' && (
<OnboardingFlow
onComplete={handleCompleteOnboarding}
onCancel={() => setCurrentView('landing')}
/>
)}
{currentView === 'listen' && (
<LiveListenScreen
selectedPet={selectedPet}
onAnalysisComplete={handleAnalysisComplete}
onUpdatePet={handleUpdatePet}
/>
)}
{currentView === 'talkback' && (
<TalkBackScreen
selectedPet={selectedPet}
onLogTalkBackReaction={handleLogTalkBackReaction}
/>
)}
{currentView === 'timeline' && (
<PetTimeline
selectedPet={selectedPet}
timelineItems={timelineItems}
onAddManualEvent={handleAddManualEvent}
/>
)}
{currentView === 'insights' && (
<InsightsScreen
selectedPet={selectedPet}
dictionarySignals={dictionarySignals}
onResetPetModel={handleResetPetModel}
onDeletePetAccount={handleDeletePetAccount}
/>
)}
</main>
{/* Footer */}
<footer className="border-t border-[#E5DFD3] py-8 bg-[#F4EFEC]/60 text-center text-xs text-[#5A6E85] space-y-2">
<div className="max-w-4xl mx-auto px-4 flex flex-col sm:flex-row items-center justify-between gap-4">
<div className="flex items-center gap-2">
<span className="font-serif font-bold text-[#1A2A40] text-sm tracking-tight">PetTalk</span>
<span>• Probabilistic AI Pet Behavioral Interpretation</span>
</div>
<div className="flex items-center gap-4 text-[#5A6E85]">
<button onClick={() => setCurrentView('landing')} className="hover:text-[#1A2A40] transition-colors cursor-pointer">About</button>
<button onClick={() => setCurrentView('insights')} className="hover:text-[#1A2A40] transition-colors cursor-pointer">Privacy & Data</button>
<button onClick={() => setCurrentView('onboarding')} className="hover:text-[#1A2A40] transition-colors cursor-pointer">Add Pet</button>
<button onClick={logout} className="hover:text-[#F08C7D] transition-colors cursor-pointer font-semibold">
Sign Out ({user?.username})
</button>
</div>
</div>
</footer>
</div>
);
}