- Add displayName field to Beneficiary type (computed: customName || name) - Populate displayName in getAllBeneficiaries and getWellNuoBeneficiary API calls - Update detail page header to use beneficiary.displayName - Update MockDashboard to use displayName 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
211 lines
6.7 KiB
TypeScript
211 lines
6.7 KiB
TypeScript
import React, { createContext, useContext, useState, useCallback, useEffect } from 'react';
|
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
import type { Beneficiary, BeneficiarySubscription } from '@/types';
|
|
|
|
const LOCAL_BENEFICIARIES_KEY = 'wellnuo_local_beneficiaries';
|
|
|
|
interface AddBeneficiaryData {
|
|
name: string;
|
|
address?: string;
|
|
avatar?: string;
|
|
subscription?: BeneficiarySubscription;
|
|
}
|
|
|
|
interface BeneficiaryContextType {
|
|
currentBeneficiary: Beneficiary | null;
|
|
setCurrentBeneficiary: (beneficiary: Beneficiary | null) => void;
|
|
clearCurrentBeneficiary: () => void;
|
|
// Local beneficiaries management (for users without API deployments)
|
|
localBeneficiaries: Beneficiary[];
|
|
addLocalBeneficiary: (data: string | AddBeneficiaryData) => Promise<Beneficiary>;
|
|
updateLocalBeneficiary: (id: number, data: Partial<Beneficiary>) => Promise<Beneficiary | null>;
|
|
removeLocalBeneficiary: (id: number) => Promise<void>;
|
|
// Clear all data (used on logout)
|
|
clearAllBeneficiaryData: () => Promise<void>;
|
|
// Helper to format beneficiary context for AI
|
|
getBeneficiaryContext: () => string;
|
|
}
|
|
|
|
const BeneficiaryContext = createContext<BeneficiaryContextType | undefined>(undefined);
|
|
|
|
export function BeneficiaryProvider({ children }: { children: React.ReactNode }) {
|
|
const [currentBeneficiary, setCurrentBeneficiary] = useState<Beneficiary | null>(null);
|
|
const [localBeneficiaries, setLocalBeneficiaries] = useState<Beneficiary[]>([]);
|
|
|
|
// Load local beneficiaries on mount
|
|
useEffect(() => {
|
|
loadLocalBeneficiaries();
|
|
}, []);
|
|
|
|
const loadLocalBeneficiaries = async () => {
|
|
try {
|
|
const stored = await AsyncStorage.getItem(LOCAL_BENEFICIARIES_KEY);
|
|
if (stored) {
|
|
setLocalBeneficiaries(JSON.parse(stored));
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to load local beneficiaries:', error);
|
|
}
|
|
};
|
|
|
|
const saveLocalBeneficiaries = async (beneficiaries: Beneficiary[]) => {
|
|
try {
|
|
await AsyncStorage.setItem(LOCAL_BENEFICIARIES_KEY, JSON.stringify(beneficiaries));
|
|
} catch (error) {
|
|
console.error('Failed to save local beneficiaries:', error);
|
|
}
|
|
};
|
|
|
|
const addLocalBeneficiary = useCallback(async (data: string | AddBeneficiaryData): Promise<Beneficiary> => {
|
|
// Support both string (legacy) and object format
|
|
const beneficiaryData: AddBeneficiaryData = typeof data === 'string'
|
|
? { name: data }
|
|
: data;
|
|
|
|
const newBeneficiary: Beneficiary = {
|
|
id: Date.now(), // Use timestamp as unique ID
|
|
name: beneficiaryData.name.trim(),
|
|
displayName: beneficiaryData.name.trim(), // For UI display
|
|
address: beneficiaryData.address?.trim(),
|
|
avatar: beneficiaryData.avatar,
|
|
status: 'offline',
|
|
last_activity: 'Just added',
|
|
subscription: beneficiaryData.subscription,
|
|
};
|
|
|
|
const updated = [...localBeneficiaries, newBeneficiary];
|
|
setLocalBeneficiaries(updated);
|
|
await saveLocalBeneficiaries(updated);
|
|
|
|
// Auto-select if first beneficiary
|
|
if (localBeneficiaries.length === 0) {
|
|
setCurrentBeneficiary(newBeneficiary);
|
|
}
|
|
|
|
return newBeneficiary;
|
|
}, [localBeneficiaries]);
|
|
|
|
const updateLocalBeneficiary = useCallback(async (id: number, data: Partial<Beneficiary>): Promise<Beneficiary | null> => {
|
|
const index = localBeneficiaries.findIndex(b => b.id === id);
|
|
if (index === -1) return null;
|
|
|
|
const updatedBeneficiary = { ...localBeneficiaries[index], ...data };
|
|
const updated = [...localBeneficiaries];
|
|
updated[index] = updatedBeneficiary;
|
|
|
|
setLocalBeneficiaries(updated);
|
|
await saveLocalBeneficiaries(updated);
|
|
|
|
// Update current if it's the same beneficiary
|
|
if (currentBeneficiary?.id === id) {
|
|
setCurrentBeneficiary(updatedBeneficiary);
|
|
}
|
|
|
|
return updatedBeneficiary;
|
|
}, [localBeneficiaries, currentBeneficiary]);
|
|
|
|
const removeLocalBeneficiary = useCallback(async (id: number) => {
|
|
const updated = localBeneficiaries.filter(b => b.id !== id);
|
|
setLocalBeneficiaries(updated);
|
|
await saveLocalBeneficiaries(updated);
|
|
|
|
// Clear current if removed
|
|
if (currentBeneficiary?.id === id) {
|
|
setCurrentBeneficiary(updated[0] || null);
|
|
}
|
|
}, [localBeneficiaries, currentBeneficiary]);
|
|
|
|
const clearCurrentBeneficiary = useCallback(() => {
|
|
setCurrentBeneficiary(null);
|
|
}, []);
|
|
|
|
// Clear all beneficiary data (called on logout)
|
|
const clearAllBeneficiaryData = useCallback(async () => {
|
|
setCurrentBeneficiary(null);
|
|
setLocalBeneficiaries([]);
|
|
await AsyncStorage.removeItem(LOCAL_BENEFICIARIES_KEY);
|
|
}, []);
|
|
|
|
const getBeneficiaryContext = useCallback(() => {
|
|
if (!currentBeneficiary) {
|
|
return '';
|
|
}
|
|
|
|
const b = currentBeneficiary;
|
|
const contextParts: string[] = [];
|
|
|
|
// Basic info
|
|
contextParts.push(`Person: ${b.name}`);
|
|
|
|
if (b.address) {
|
|
contextParts.push(`Address: ${b.address}`);
|
|
}
|
|
|
|
// Current status
|
|
if (b.last_location) {
|
|
contextParts.push(`Current location: ${b.last_location}`);
|
|
}
|
|
|
|
if (b.before_last_location) {
|
|
contextParts.push(`Previous location: ${b.before_last_location}`);
|
|
}
|
|
|
|
// Health metrics
|
|
if (b.wellness_score !== undefined) {
|
|
contextParts.push(`Wellness score: ${b.wellness_score}% (${b.wellness_descriptor || 'N/A'})`);
|
|
}
|
|
|
|
// Temperature
|
|
if (b.temperature !== undefined) {
|
|
const unit = b.units || '°F';
|
|
contextParts.push(`Room temperature: ${b.temperature.toFixed(1)}${unit}`);
|
|
}
|
|
|
|
if (b.bedroom_temperature !== undefined) {
|
|
const unit = b.units || '°F';
|
|
contextParts.push(`Bedroom temperature: ${b.bedroom_temperature.toFixed(1)}${unit}`);
|
|
}
|
|
|
|
// Sleep data
|
|
if (b.sleep_hours !== undefined) {
|
|
contextParts.push(`Sleep hours: ${b.sleep_hours.toFixed(1)} hours`);
|
|
}
|
|
|
|
// Activity time
|
|
if (b.last_detected_time) {
|
|
contextParts.push(`Last detected: ${b.last_detected_time}`);
|
|
}
|
|
|
|
// Status
|
|
contextParts.push(`Status: ${b.status === 'online' ? 'Active' : 'Inactive'}`);
|
|
|
|
return `[SENSOR DATA FOR ${b.name.toUpperCase()}: ${contextParts.join('. ')}]`;
|
|
}, [currentBeneficiary]);
|
|
|
|
return (
|
|
<BeneficiaryContext.Provider
|
|
value={{
|
|
currentBeneficiary,
|
|
setCurrentBeneficiary,
|
|
clearCurrentBeneficiary,
|
|
localBeneficiaries,
|
|
addLocalBeneficiary,
|
|
updateLocalBeneficiary,
|
|
removeLocalBeneficiary,
|
|
clearAllBeneficiaryData,
|
|
getBeneficiaryContext,
|
|
}}
|
|
>
|
|
{children}
|
|
</BeneficiaryContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function useBeneficiary() {
|
|
const context = useContext(BeneficiaryContext);
|
|
if (context === undefined) {
|
|
throw new Error('useBeneficiary must be used within a BeneficiaryProvider');
|
|
}
|
|
return context;
|
|
}
|