WellNuo/contexts/BeneficiaryContext.tsx
Sergei 9f0baea3fd feat(beneficiaries): Use displayName in detail page header
- 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>
2026-01-22 12:54:50 -08:00

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;
}