Snapshot: full project state
This commit is contained in:
197
pettalk-App-new.tsx
Normal file
197
pettalk-App-new.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user