Skip to content

Commit 8518ccc

Browse files
Complete viewport implementation with journal & dashboard functionality
Co-authored-by: gitcoder89431 <211172822+gitcoder89431@users.noreply.github.com>
1 parent 2142d36 commit 8518ccc

File tree

3 files changed

+463
-27
lines changed

3 files changed

+463
-27
lines changed
Lines changed: 363 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,365 @@
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

Comments
 (0)