diff --git a/pawsense/App.js b/pawsense/App.js
index 0a0ca20..bc8395f 100644
--- a/pawsense/App.js
+++ b/pawsense/App.js
@@ -1,22 +1,21 @@
-// App.js
-import React, { useState, useEffect } from 'react';
+import { useState, useEffect } from 'react';
import {
- StyleSheet,
Text,
View,
- ScrollView,
- TouchableOpacity,
- TextInput,
SafeAreaView,
StatusBar,
- Dimensions,
Alert
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
-import styles from './AppStyles';
-const { width } = Dimensions.get('window');
+import styles from './AppStyles.js';
+import { TabButton } from './components/components.jsx';
+import Dashboard from "./components/Dashboard.jsx";
+import Health from "./components/Health.jsx";
+import Location from "./components/Location.jsx";
+import Behavior from "./components/Behavior.jsx";
+import Translator from "./components/Translator.jsx";
// Mock data generators
const generateHealthData = () => ({
@@ -35,44 +34,6 @@ const locations = [
{ name: 'Vet Clinic', lat: 40.7505, lng: -73.9934 }
];
-// Reusable Components
-const Card = ({ children, style }) => (
-
- {children}
-
-);
-
-const Badge = ({ text, style, textStyle }) => (
-
- {text}
-
-);
-
-const ProgressBar = ({ progress, color = '#3B82F6', height = 8 }) => (
-
-
-
-);
-
-const TabButton = ({ icon, isActive, onPress, label }) => (
-
-
- {label && {label}}
-
-);
-
export default function SmartDogCollarApp() {
const [currentTab, setCurrentTab] = useState('dashboard');
const [healthData, setHealthData] = useState(generateHealthData());
@@ -103,14 +64,14 @@ export default function SmartDogCollarApp() {
return () => clearInterval(interval);
}, []);
- //
+ //
const handleTranslate = () => {
if (!translatorInput.trim()) {
Alert.alert('Please enter a message');
return;
}
-
+
const responses = [
'Woof woof! (Time for a walk!)',
'Bark bark woof! (I love you too!)',
@@ -132,7 +93,7 @@ export default function SmartDogCollarApp() {
return emojiMap[behavior] || '🐕';
};
-
+
useEffect(() => {
async function fetchPrediction() {
try {
@@ -170,7 +131,7 @@ export default function SmartDogCollarApp() {
console.error('Error fetching prediction:', error);
}
}
-
+
fetchPrediction();
}, []);
@@ -193,326 +154,17 @@ export default function SmartDogCollarApp() {
console.error('Error fetching client data:', error);
}
}
-
+
fetchClientData();
}, []);
// console.log('Predictions: ', predictions)
// console.log('Predictions: ', clientData)
- const renderDashboard = () => (
-
-
- Current Status
-
-
- {clientData.heart_rate}
- BPM
-
-
- {clientData.temperature}°F
- Temperature
-
-
-
- Activity Level
-
-
-
-
-
-
- Behavior & Emotion
-
-
- {clientData.activity}
- Current Activity
-
-
-
-
- Last updated: {new Date().toLocaleTimeString()}
-
-
-
-
- Location
-
-
- {clientData.context}
-
- {clientData.latitude}, {clientData.longitude}
-
-
-
-
-
-
- );
-
- const renderHealth = () => (
-
-
- Vital Signs
-
-
-
-
- Heart Rate
-
- {clientData.heart_rate}
- BPM (Normal)
-
-
-
-
- Temperature
-
- {clientData.temperature}°F
- Normal Range
-
-
-
-
-
- Daily Activity
-
-
- Steps Today
- {clientData.steps}
-
-
- Goal: 15,000 steps
-
-
-
- Calories Burned
- {clientData.calories}
-
-
- Goal: 600 calories
-
-
-
-
- Health Alerts
-
-
- ⚠️ Health Alert!
-
-
- Max has missed his medicine dose today.
-
-
-
-
- );
-
- const renderLocation = () => (
-
-
- Live Location
-
-
- {clientData.context}
-
- {clientData.latitude}, {clientData.longitude}
-
-
-
-
-
- Distance from Home
- 0.3 miles
-
-
- Last Movement
- 2 minutes ago
-
-
-
-
-
- Recent Locations
- {locations.map((location, index) => (
-
-
-
-
- {location.name}
-
- {Math.floor(Math.random() * 60)} min ago
-
-
-
-
-
- ))}
-
-
- );
-
- const renderBehavior = () => (
-
-
- Current Behavior
-
- {getBehaviorEmoji(currentBehavior)}
- {currentBehavior}
- Confidence: 94%
-
-
-
-
-
-
- Activity Predictions
-
- {predictions.length > 0 ? (
- [...predictions] // create a shallow copy to avoid mutating state directly
- .sort((a, b) => {
- // extract minutes from timeInfo like "in 123 minutes"
- const aMinutes = parseInt(a.timeInfo.match(/in (\d+) minutes/)[1], 10);
- const bMinutes = parseInt(b.timeInfo.match(/in (\d+) minutes/)[1], 10);
- return aMinutes - bMinutes;
- })
- .map((pred, idx) => {
- const [hour, minute] = pred.time.split(':');
-
- const minutesMatch = pred.timeInfo.match(/in (\d+) minutes/);
- let timeInfoFormatted = pred.timeInfo;
- if (minutesMatch) {
- const totalMinutes = parseInt(minutesMatch[1], 10);
- if (totalMinutes >= 60) {
- const hrs = Math.floor(totalMinutes / 60);
- const mins = totalMinutes % 60;
- timeInfoFormatted = `in ${hrs} hour${hrs > 1 ? 's' : ''} ${mins} minute${mins !== 1 ? 's' : ''}`;
- }
- }
-
- return (
-
- {pred.label}
-
-
- );
- })
- ) : (
- Loading predictions...
- )}
-
-
- );
-
- const renderTranslator = () => (
-
-
- Two-Way Translator
-
- Speak to Max:
-
-
-
-
-
-
-
- {translatorOutput && (
-
- Max's Response:
- {translatorOutput}
-
- )}
-
-
-
- Recent Bark Translations
- {recentBarks.map((bark, index) => (
-
-
- {bark.translation}
-
-
- {bark.time}
-
- ))}
-
-
-
- Quick Commands
-
- {['Sit', 'Stay', 'Come', 'Good Boy'].map((command) => (
- {
- setTranslatorInput(command);
- handleTranslate();
- }}
- >
- {command}
-
- ))}
-
-
-
- );
-
- const renderTabContent = () => {
- switch (currentTab) {
- case 'dashboard':
- return renderDashboard();
- case 'health':
- return renderHealth();
- case 'location':
- return renderLocation();
- case 'behavior':
- return renderBehavior();
- case 'translator':
- return renderTranslator();
- default:
- return renderDashboard();
- }
- };
-
return (
-
+
{/* Header */}
-
{Math.floor(batteryLevel)}%
@@ -546,35 +198,50 @@ export default function SmartDogCollarApp() {
{/* Tab Navigation */}
- setCurrentTab('dashboard')}
/>
- setCurrentTab('health')}
/>
- setCurrentTab('location')}
/>
- setCurrentTab('behavior')}
/>
- setCurrentTab('translator')}
/>
- {/* Tab Content */}
- {renderTabContent()}
+ {(() => {
+ const DashboardBound = Dashboard.bind(this, healthData, clientData, currentEmotion)
+ switch (currentTab) {
+ case 'dashboard':
+ return DashboardBound();
+ case 'health':
+ return Health(clientData);
+ case 'location':
+ return Location(clientData, locations);
+ case 'behavior':
+ return Behavior(getBehaviorEmoji, currentBehavior, currentEmotion, predictions);
+ case 'translator':
+ return Translator(translatorInput, setTranslatorInput, handleTranslate, translatorOutput, recentBarks);
+ default:
+ return DashboardBound();
+ }
+ })()}
);
-}
\ No newline at end of file
+}
diff --git a/pawsense/AppStyles.js b/pawsense/AppStyles.js
index a120117..5549268 100644
--- a/pawsense/AppStyles.js
+++ b/pawsense/AppStyles.js
@@ -1,9 +1,5 @@
-// styles.js
import { StyleSheet, Dimensions } from 'react-native';
-const { width, height } = Dimensions.get('window');
-
-
const styles = StyleSheet.create({
container: {
flex: 1,
@@ -497,7 +493,7 @@ const styles = StyleSheet.create({
borderRadius: 8,
paddingHorizontal: 16,
paddingVertical: 8,
- minWidth: (width - 64) / 2 - 4,
+ minWidth: (Dimensions.get('window').width - 64) / 2 - 4,
alignItems: 'center',
},
commandButtonText: {
diff --git a/pawsense/components/Behavior.jsx b/pawsense/components/Behavior.jsx
new file mode 100644
index 0000000..52abc9c
--- /dev/null
+++ b/pawsense/components/Behavior.jsx
@@ -0,0 +1,62 @@
+import { ScrollView, Text, View } from "react-native";
+import { Card, Badge } from "./components";
+import styles from "../AppStyles";
+
+export default (getBehaviorEmoji, currentBehavior, currentEmotion, predictions) => (
+
+
+ Current Behavior
+
+ {getBehaviorEmoji(currentBehavior)}
+ {currentBehavior}
+ Confidence: 94%
+
+
+
+
+
+
+ Activity Predictions
+
+ {predictions.length > 0 ? (
+ [...predictions] // create a shallow copy to avoid mutating state directly
+ .sort((a, b) => {
+ // extract minutes from timeInfo like "in 123 minutes"
+ const aMinutes = parseInt(a.timeInfo.match(/in (\d+) minutes/)[1], 10);
+ const bMinutes = parseInt(b.timeInfo.match(/in (\d+) minutes/)[1], 10);
+ return aMinutes - bMinutes;
+ })
+ .map((pred, idx) => {
+ const [hour, minute] = pred.time.split(':');
+
+ const minutesMatch = pred.timeInfo.match(/in (\d+) minutes/);
+ let timeInfoFormatted = pred.timeInfo;
+ if (minutesMatch) {
+ const totalMinutes = parseInt(minutesMatch[1], 10);
+ if (totalMinutes >= 60) {
+ const hrs = Math.floor(totalMinutes / 60);
+ const mins = totalMinutes % 60;
+ timeInfoFormatted = `in ${hrs} hour${hrs > 1 ? 's' : ''} ${mins} minute${mins !== 1 ? 's' : ''}`;
+ }
+ }
+
+ return (
+
+ {pred.label}
+
+
+ );
+ })
+ ) : (
+ Loading predictions...
+ )}
+
+
+);
diff --git a/pawsense/components/Dashboard.jsx b/pawsense/components/Dashboard.jsx
new file mode 100644
index 0000000..543f34f
--- /dev/null
+++ b/pawsense/components/Dashboard.jsx
@@ -0,0 +1,61 @@
+import { ScrollView, Text, View } from "react-native";
+import styles from "../AppStyles.js";
+import { Card, Badge, ProgressBar } from "./components.jsx";
+
+export default (healthData, clientData, currentEmotion) => (
+
+
+ Current Status
+
+
+ {clientData.heart_rate}
+ BPM
+
+
+ {clientData.temperature}°F
+ Temperature
+
+
+
+ Activity Level
+
+
+
+
+
+
+ Behavior & Emotion
+
+
+ {clientData.activity}
+ Current Activity
+
+
+
+
+ Last updated: {new Date().toLocaleTimeString()}
+
+
+
+
+ Location
+
+
+ {clientData.context}
+
+ {clientData.latitude}, {clientData.longitude}
+
+
+
+
+
+
+);
diff --git a/pawsense/components/Health.jsx b/pawsense/components/Health.jsx
new file mode 100644
index 0000000..a3a8fdb
--- /dev/null
+++ b/pawsense/components/Health.jsx
@@ -0,0 +1,63 @@
+// @ts-check
+import { ScrollView, Text, View } from "react-native";
+import { Card, ProgressBar } from "./components";
+import styles from "../AppStyles";
+import { Ionicons } from "@expo/vector-icons";
+
+export default (clientData) => (
+
+
+ Vital Signs
+
+
+
+
+ Heart Rate
+
+ {clientData.heart_rate}
+ BPM (Normal)
+
+
+
+
+ Temperature
+
+ {clientData.temperature}°F
+ Normal Range
+
+
+
+
+
+ Daily Activity
+
+
+ Steps Today
+ {clientData.steps}
+
+
+ Goal: 15,000 steps
+
+
+
+ Calories Burned
+ {clientData.calories}
+
+
+ Goal: 600 calories
+
+
+
+
+ Health Alerts
+
+
+ ⚠️ Health Alert!
+
+
+ Max has missed his medicine dose today.
+
+
+
+
+);
diff --git a/pawsense/components/Location.jsx b/pawsense/components/Location.jsx
new file mode 100644
index 0000000..df55a88
--- /dev/null
+++ b/pawsense/components/Location.jsx
@@ -0,0 +1,55 @@
+import { ScrollView, Text, View } from "react-native";
+import { Ionicons } from "@expo/vector-icons";
+import { Card, Badge } from "./components";
+import styles from "../AppStyles";
+
+export default (clientData, locations) => (
+
+
+ Live Location
+
+
+ {clientData.context}
+
+ {clientData.latitude}, {clientData.longitude}
+
+
+
+
+
+ Distance from Home
+ 0.3 miles
+
+
+ Last Movement
+ 2 minutes ago
+
+
+
+
+
+ Recent Locations
+ {locations.map((location, index) => (
+
+
+
+
+ {location.name}
+
+ {Math.floor(Math.random() * 60)} min ago
+
+
+
+
+
+ ))}
+
+
+);
diff --git a/pawsense/components/Translator.jsx b/pawsense/components/Translator.jsx
new file mode 100644
index 0000000..a802121
--- /dev/null
+++ b/pawsense/components/Translator.jsx
@@ -0,0 +1,70 @@
+import { ScrollView, Text, View, TouchableOpacity, TextInput } from "react-native";
+import { Ionicons } from "@expo/vector-icons";
+import styles from "../AppStyles";
+import { Card, Badge } from "./components";
+
+export default (translatorInput, setTranslatorInput, handleTranslate, translatorOutput, recentBarks) => (
+
+
+ Two-Way Translator
+
+ Speak to Max:
+
+
+
+
+
+
+
+ {translatorOutput && (
+
+ Max's Response:
+ {translatorOutput}
+
+ )}
+
+
+
+ Recent Bark Translations
+ {recentBarks.map((bark, index) => (
+
+
+ {bark.translation}
+
+
+ {bark.time}
+
+ ))}
+
+
+
+ Quick Commands
+
+ {['Sit', 'Stay', 'Come', 'Good Boy'].map((command) => (
+ {
+ setTranslatorInput(command);
+ handleTranslate();
+ }}
+ >
+ {command}
+
+ ))}
+
+
+
+);
diff --git a/pawsense/components/components.jsx b/pawsense/components/components.jsx
new file mode 100644
index 0000000..1b82728
--- /dev/null
+++ b/pawsense/components/components.jsx
@@ -0,0 +1,40 @@
+import { View, Text, TouchableOpacity } from "react-native";
+import { Ionicons } from "@expo/vector-icons";
+import styles from "../AppStyles";
+
+export const Card = ({ children, style }) => (
+
+ {children}
+
+);
+
+export const Badge = ({ text, style, textStyle }) => (
+
+ {text}
+
+);
+
+export const ProgressBar = ({ progress, color = '#3B82F6', height = 8 }) => (
+
+
+
+);
+
+export const TabButton = ({ icon, isActive, onPress, label }) => (
+
+
+ {label && {label}}
+
+);