1- <script lang =" ts" >
2- // Main viewport panel for primary content
1+ <script >
2+ import { onMount } from ' svelte' ;
3+ import { selectedPetStore , petHelpers , selectedPetHelpers } from ' $lib/stores/pets' ;
4+ import { aiAnalysisHelpers , isAnalyzing } from ' $lib/stores/ai-analysis' ;
5+ import { PenTool , Brain , Calendar , TrendingUp , Heart , Activity } from ' lucide-svelte' ;
6+ import AIInsightsCard from ' ../ui/AIInsightsCard.svelte' ;
7+
8+ let selectedPetId = null ;
9+ let selectedPet = null ;
10+ let currentView = ' dashboard' ; // dashboard, journal, history
11+ let journalInput = ' ' ;
12+ let selectedMood = ' ' ;
13+ let selectedActivity = ' ' ;
14+ let isSubmitting = false ;
15+
16+ onMount (() => {
17+ // Load pets and selected pet from storage
18+ petHelpers .load ();
19+ selectedPetHelpers .load ();
20+
21+ selectedPetStore .subscribe (petId => {
22+ selectedPetId = petId;
23+ selectedPet = petId ? petHelpers .getPet (petId) : null ;
24+ });
25+ });
26+
27+ async function submitJournalEntry () {
28+ if (! journalInput .trim () || ! selectedPet) return ;
29+
30+ isSubmitting = true ;
31+
32+ try {
33+ const entry = {
34+ id: Date .now ().toString (),
35+ petId: selectedPet .id ,
36+ content: journalInput .trim (),
37+ date: new Date ().toISOString (),
38+ mood: selectedMood || ' unknown' ,
39+ activityLevel: selectedActivity || ' normal'
40+ };
41+
42+ // Add entry to pet
43+ petHelpers .addJournalEntry (selectedPet .id , entry);
44+
45+ // Analyze with AI (non-blocking)
46+ try {
47+ await aiAnalysisHelpers .analyzeEntry (selectedPet, entry);
48+ } catch (error) {
49+ console .error (' AI analysis failed:' , error);
50+ }
51+
52+ // Reset form
53+ journalInput = ' ' ;
54+ selectedMood = ' ' ;
55+ selectedActivity = ' ' ;
56+ currentView = ' dashboard' ;
57+
58+ // Refresh selected pet data
59+ selectedPet = petHelpers .getPet (selectedPet .id );
60+ } catch (error) {
61+ console .error (' Error submitting entry:' , error);
62+ } finally {
63+ isSubmitting = false ;
64+ }
65+ }
366 </script >
467
5- <div class =" h-full p-6 flex flex-col" >
6- <div class =" panel-header mb-4" >
7- <h2 class =" text-lg font-semibold" style =" color: var(--petalytics-text);" >
8- Main Viewport
9- </h2 >
10- <p class =" text-sm mt-1" style =" color: var(--petalytics-subtle);" >
11- Primary content and interactions
12- </p >
13- </div >
14-
15- <div class =" flex-1 flex items-center justify-center" >
16- <div class =" text-center" >
17- <div class =" w-20 h-20 rounded-lg mx-auto mb-4 flex items-center justify-center"
18- style =" background: var(--petalytics-highlight-med);" >
19- <span class =" text-3xl" >📊</span >
20- </div >
21- <p class =" text-sm" style =" color: var(--petalytics-subtle);" >
22- Main viewport placeholder
23- </p >
24- <p class =" text-xs mt-2" style =" color: var(--petalytics-muted);" >
25- This is where the primary application content will be displayed
26- </p >
27- </div >
28- </div >
29- </div >
68+ <div class =" viewport-container h-full flex flex-col" >
69+ {#if ! selectedPet }
70+ <!-- Welcome Screen -->
71+ <div class =" welcome-screen h-full flex items-center justify-center p-8" >
72+ <div class =" text-center max-w-lg" >
73+ <div class =" w-24 h-24 mx-auto mb-6 rounded-full bg-gradient-to-r from-blue-400 to-purple-500 flex items-center justify-center" >
74+ <Heart size ={40 } class =" text-white" />
75+ </div >
76+ <h2 class =" text-2xl font-bold mb-4" style =" color: var(--petalytics-text);" >
77+ Welcome to Petalytics
78+ </h2 >
79+ <p class =" text-lg mb-6" style =" color: var(--petalytics-subtle);" >
80+ Select a pet to start journaling and get AI-powered insights about their well-being.
81+ </p >
82+ </div >
83+ </div >
84+ {:else }
85+ <!-- Pet Dashboard -->
86+ <div class =" pet-viewport h-full flex flex-col" >
87+ <!-- Header with pet info and navigation -->
88+ <div class =" viewport-header p-4 border-b" style =" border-color: var(--petalytics-border);" >
89+ <div class =" flex items-center justify-between" >
90+ <div class =" flex items-center space-x-3" >
91+ <img
92+ src ={selectedPet .profileImageUrl || ' /images/default-pet.png' }
93+ alt ={selectedPet .name }
94+ class =" w-12 h-12 rounded-full object-cover"
95+ />
96+ <div >
97+ <h2 class =" text-xl font-bold" style =" color: var(--petalytics-text);" >
98+ {selectedPet .name }
99+ </h2 >
100+ <p class =" text-sm" style =" color: var(--petalytics-subtle);" >
101+ {selectedPet .age } year old {selectedPet .breed }
102+ </p >
103+ </div >
104+ </div >
105+
106+ <div class =" flex space-x-2" >
107+ <button
108+ on:click ={() => currentView = ' dashboard' }
109+ class =" nav-button px-3 py-1 rounded-md text-sm"
110+ class:active ={currentView === ' dashboard' }
111+ >
112+ Dashboard
113+ </button >
114+ <button
115+ on:click ={() => currentView = ' journal' }
116+ class =" nav-button px-3 py-1 rounded-md text-sm"
117+ class:active ={currentView === ' journal' }
118+ >
119+ New Entry
120+ </button >
121+ <button
122+ on:click ={() => currentView = ' history' }
123+ class =" nav-button px-3 py-1 rounded-md text-sm"
124+ class:active ={currentView === ' history' }
125+ >
126+ History
127+ </button >
128+ </div >
129+ </div >
130+ </div >
131+
132+ <!-- Content Area -->
133+ <div class =" viewport-content flex-1 p-4 overflow-y-auto" >
134+ {#if currentView === ' dashboard' }
135+ <!-- Dashboard View -->
136+ <div class =" dashboard-grid space-y-4" >
137+ <!-- Stats Cards -->
138+ <div class =" stats-grid grid grid-cols-2 md:grid-cols-4 gap-4" >
139+ <div class =" stat-card p-4 rounded-lg text-center" style =" background: var(--petalytics-surface);" >
140+ <div class =" text-2xl font-bold" style =" color: var(--petalytics-accent);" >
141+ {selectedPet .journalEntries .length }
142+ </div >
143+ <div class =" text-xs" style =" color: var(--petalytics-subtle);" >Total Entries</div >
144+ </div >
145+ <div class =" stat-card p-4 rounded-lg text-center" style =" background: var(--petalytics-surface);" >
146+ <div class =" text-2xl font-bold" style =" color: var(--petalytics-accent);" >
147+ {selectedPet .age }
148+ </div >
149+ <div class =" text-xs" style =" color: var(--petalytics-subtle);" >Years Old</div >
150+ </div >
151+ <div class =" stat-card p-4 rounded-lg text-center" style =" background: var(--petalytics-surface);" >
152+ <div class =" text-2xl font-bold" style =" color: var(--petalytics-accent);" >
153+ {Math .max (0 , Math .floor ((Date .now () - new Date (selectedPet .createdAt ).getTime ()) / (1000 * 60 * 60 * 24 )))}
154+ </div >
155+ <div class =" text-xs" style =" color: var(--petalytics-subtle);" >Days Tracked</div >
156+ </div >
157+ <div class =" stat-card p-4 rounded-lg text-center" style =" background: var(--petalytics-surface);" >
158+ <div class =" text-2xl font-bold" style =" color: var(--petalytics-accent);" >
159+ {selectedPet .breed }
160+ </div >
161+ <div class =" text-xs" style =" color: var(--petalytics-subtle);" >Breed</div >
162+ </div >
163+ </div >
164+
165+ <!-- Recent Activity & AI Insights -->
166+ <div class =" content-grid grid grid-cols-1 lg:grid-cols-2 gap-4" >
167+ <div class =" activity-card p-4 rounded-lg" style =" background: var(--petalytics-surface);" >
168+ <h3 class =" font-semibold mb-3 flex items-center" style =" color: var(--petalytics-text);" >
169+ <Activity size ={16 } class =" mr-2" style =" color: var(--petalytics-accent);" />
170+ Recent Activity
171+ </h3 >
172+ {#if selectedPet .journalEntries .length === 0 }
173+ <p class =" text-sm" style =" color: var(--petalytics-subtle);" >
174+ No entries yet. Click "New Entry" to start journaling!
175+ </p >
176+ {:else }
177+ <div class =" space-y-2" >
178+ {#each selectedPet .journalEntries .slice (- 3 ).reverse () as entry }
179+ <div class =" entry-preview p-2 rounded" style =" background: var(--petalytics-overlay);" >
180+ <div class =" flex justify-between items-center mb-1" >
181+ <span class =" text-xs" style =" color: var(--petalytics-subtle);" >
182+ {new Date (entry .date ).toLocaleDateString ()}
183+ </span >
184+ <span class =" text-sm" >
185+ {entry .mood === ' happy' ? ' 😊' :
186+ entry .mood === ' sad' ? ' 😢' :
187+ entry .mood === ' playful' ? ' 🎾' :
188+ entry .mood === ' tired' ? ' 😴' : ' 🐾' }
189+ </span >
190+ </div >
191+ <p class =" text-sm truncate" style =" color: var(--petalytics-text);" >
192+ {entry .content }
193+ </p >
194+ </div >
195+ {/each }
196+ </div >
197+ {/if }
198+ </div >
199+
200+ <div class =" insights-card p-4 rounded-lg" style =" background: var(--petalytics-surface);" >
201+ <h3 class =" font-semibold mb-3 flex items-center" style =" color: var(--petalytics-text);" >
202+ <Brain size ={16 } class =" mr-2" style =" color: var(--petalytics-accent);" />
203+ AI Insights
204+ </h3 >
205+ {#if selectedPet .journalEntries .length === 0 }
206+ <p class =" text-sm" style =" color: var(--petalytics-subtle);" >
207+ Add journal entries to get AI-powered insights about {selectedPet .name }'s well-being.
208+ </p >
209+ {:else }
210+ <AIInsightsCard petId ={selectedPet .id } />
211+ {/if }
212+ </div >
213+ </div >
214+ </div >
215+
216+ {:else if currentView === ' journal' }
217+ <!-- Journal Entry Form -->
218+ <div class =" journal-form max-w-2xl mx-auto" >
219+ <div class =" form-card p-6 rounded-lg" style =" background: var(--petalytics-surface);" >
220+ <h3 class =" text-xl font-semibold mb-4" style =" color: var(--petalytics-text);" >
221+ New Entry for {selectedPet .name }
222+ </h3 >
223+
224+ <div class =" space-y-4" >
225+ <!-- Mood & Activity Selectors -->
226+ <div class =" grid grid-cols-2 gap-4" >
227+ <div >
228+ <label class =" block text-sm font-medium mb-2" style =" color: var(--petalytics-subtle);" >
229+ Mood
230+ </label >
231+ <select bind:value ={selectedMood } class =" input w-full" >
232+ <option value =" " >Select mood...</option >
233+ <option value =" happy" >😊 Happy</option >
234+ <option value =" playful" >🎾 Playful</option >
235+ <option value =" tired" >😴 Tired</option >
236+ <option value =" anxious" >😰 Anxious</option >
237+ <option value =" sad" >😢 Sad</option >
238+ <option value =" sick" >🤒 Unwell</option >
239+ </select >
240+ </div >
241+
242+ <div >
243+ <label class =" block text-sm font-medium mb-2" style =" color: var(--petalytics-subtle);" >
244+ Activity Level
245+ </label >
246+ <select bind:value ={selectedActivity } class =" input w-full" >
247+ <option value =" " >Select activity...</option >
248+ <option value =" low" >Low Activity</option >
249+ <option value =" normal" >Normal Activity</option >
250+ <option value =" high" >High Activity</option >
251+ <option value =" unusual" >Unusual Behavior</option >
252+ </select >
253+ </div >
254+ </div >
255+
256+ <!-- Journal Text -->
257+ <div >
258+ <label class =" block text-sm font-medium mb-2" style =" color: var(--petalytics-subtle);" >
259+ What happened today?
260+ </label >
261+ <textarea
262+ bind:value ={journalInput }
263+ placeholder =" Describe your pet's behavior, health, activities, or anything noteworthy..."
264+ class =" input w-full h-32 resize-none"
265+ disabled ={isSubmitting }
266+ ></textarea >
267+ </div >
268+
269+ <!-- Actions -->
270+ <div class =" flex justify-end space-x-3" >
271+ <button
272+ on:click ={() => currentView = ' dashboard' }
273+ class =" button-secondary"
274+ disabled ={isSubmitting }
275+ >
276+ Cancel
277+ </button >
278+ <button
279+ on:click ={submitJournalEntry }
280+ class =" button flex items-center space-x-2"
281+ disabled ={! journalInput .trim () || isSubmitting }
282+ >
283+ {#if isSubmitting || $isAnalyzing }
284+ <div class =" animate-spin w-4 h-4 border-2 border-current border-t-transparent rounded-full" ></div >
285+ <span >Analyzing...</span >
286+ {:else }
287+ <PenTool size ={16 } />
288+ <span >Add Entry</span >
289+ {/if }
290+ </button >
291+ </div >
292+ </div >
293+ </div >
294+ </div >
295+
296+ {:else if currentView === ' history' }
297+ <!-- History View -->
298+ <div class =" history-view max-w-4xl mx-auto" >
299+ <h3 class =" text-xl font-semibold mb-4" style =" color: var(--petalytics-text);" >
300+ Journal History
301+ </h3 >
302+
303+ {#if selectedPet .journalEntries .length === 0 }
304+ <div class =" empty-state text-center py-12" >
305+ <Calendar size ={48 } style =" color: var(--petalytics-subtle);" class =" mx-auto mb-4" />
306+ <p class =" text-lg mb-2" style =" color: var(--petalytics-text);" >No entries yet</p >
307+ <button on:click ={() => currentView = ' journal' } class =" button" >
308+ Write First Entry
309+ </button >
310+ </div >
311+ {:else }
312+ <div class =" entries-list space-y-4" >
313+ {#each [... selectedPet .journalEntries ].reverse () as entry }
314+ <div class =" entry-card p-4 rounded-lg border" style =" background: var(--petalytics-surface); border-color: var(--petalytics-border);" >
315+ <div class =" flex justify-between items-start mb-2" >
316+ <span class =" text-sm font-medium" style =" color: var(--petalytics-text);" >
317+ {new Date (entry .date ).toLocaleDateString (' en-US' , {
318+ weekday: ' short' ,
319+ month: ' short' ,
320+ day: ' numeric'
321+ })}
322+ </span >
323+ <div class =" flex items-center space-x-2" >
324+ <span class =" text-lg" >
325+ {entry .mood === ' happy' ? ' 😊' :
326+ entry .mood === ' sad' ? ' 😢' :
327+ entry .mood === ' playful' ? ' 🎾' :
328+ entry .mood === ' tired' ? ' 😴' : ' 🐾' }
329+ </span >
330+ <span class =" text-xs px-2 py-1 rounded" style =" background: var(--petalytics-overlay); color: var(--petalytics-subtle);" >
331+ {entry .activityLevel }
332+ </span >
333+ </div >
334+ </div >
335+ <p class =" text-sm mb-3" style =" color: var(--petalytics-text);" >
336+ {entry .content }
337+ </p >
338+ <AIInsightsCard entryId ={entry .id } compact ={true } />
339+ </div >
340+ {/each }
341+ </div >
342+ {/if }
343+ </div >
344+ {/if }
345+ </div >
346+ </div >
347+ {/if }
348+ </div >
349+
350+ <style >
351+ .nav-button {
352+ background : var (--petalytics-overlay );
353+ color : var (--petalytics-text );
354+ transition : all 0.2s ;
355+ }
356+
357+ .nav-button.active {
358+ background : var (--petalytics-accent );
359+ color : var (--petalytics-bg );
360+ }
361+
362+ .nav-button :hover:not (.active ) {
363+ opacity : 0.8 ;
364+ }
365+ </style >
0 commit comments