Sergei 7cb07c09ce Major UI/UX updates: Voice, Subscription, Beneficiaries, Profile
- Voice tab: simplified interface, voice picker improvements
- Subscription: Stripe integration, purchase flow updates
- Beneficiaries: dashboard, sharing, improved management
- Profile: drawer, edit, help, privacy sections
- Theme: expanded constants, new colors
- New components: MockDashboard, ProfileDrawer, Toast
- Backend: Stripe routes additions
- Auth: activate, add-loved-one, purchase screens
2025-12-29 15:36:44 -08:00

318 lines
10 KiB
TypeScript

import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
TouchableOpacity,
TextInput,
Linking,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { SafeAreaView } from 'react-native-safe-area-context';
import { AppColors, BorderRadius, FontSizes, Spacing, FontWeights } from '@/constants/theme';
import { PageHeader } from '@/components/PageHeader';
interface FAQItemProps {
question: string;
answer: string;
isExpanded: boolean;
onToggle: () => void;
}
function FAQItem({ question, answer, isExpanded, onToggle }: FAQItemProps) {
return (
<TouchableOpacity style={styles.faqItem} onPress={onToggle} activeOpacity={0.7}>
<View style={styles.faqHeader}>
<Text style={styles.faqQuestion}>{question}</Text>
<Ionicons
name={isExpanded ? 'chevron-up' : 'chevron-down'}
size={20}
color={AppColors.textMuted}
/>
</View>
{isExpanded && (
<Text style={styles.faqAnswer}>{answer}</Text>
)}
</TouchableOpacity>
);
}
export default function HelpScreen() {
const [searchQuery, setSearchQuery] = useState('');
const [expandedFAQ, setExpandedFAQ] = useState<number | null>(null);
const faqs = [
{
question: 'How do I add a new beneficiary?',
answer: 'Tap the "+" button on the Home screen or go to Add Loved One. Enter their name, activate your WellNuo kit with the 6-digit code, and follow the setup instructions.',
},
{
question: 'What does the wellness score mean?',
answer: 'The wellness score (0-100%) reflects the overall health pattern of your beneficiary based on daily activities, sleep patterns, movement data, and sensor readings. A score above 70% indicates healthy patterns, 40-70% suggests some concerns, and below 40% may require attention.',
},
{
question: 'How often is the data updated?',
answer: 'Sensor data is collected in real-time and synced every few minutes. Dashboard summaries are calculated daily. Emergency alerts are instant and will notify you immediately.',
},
{
question: 'What triggers an emergency alert?',
answer: 'Emergency alerts are triggered by: falls detected by motion sensors, prolonged inactivity exceeding normal patterns, SOS button press by the beneficiary, and abnormal environmental readings.',
},
{
question: 'Can I share access with family members?',
answer: 'Yes! Multiple family members can monitor the same beneficiary. Each caregiver will have their own account and can set their own notification preferences.',
},
{
question: 'How do I change notification settings?',
answer: 'Go to Profile > Settings to customize your alert preferences. You can enable or disable push notifications and email alerts.',
},
{
question: 'Is my data secure?',
answer: 'Yes. WellNuo uses encryption for all data transmission. Your data is stored securely, and we never share personal information with third parties.',
},
{
question: 'What devices are in the Starter Kit?',
answer: 'The WellNuo Starter Kit includes: motion sensor (PIR), door/window sensor, temperature & humidity sensor, and the WellNuo Hub that connects everything.',
},
{
question: 'How do I activate my kit?',
answer: 'After purchase, you\'ll receive a 6-digit activation code. Go to Add Loved One, enter the code, and the app will guide you through connecting each sensor.',
},
{
question: 'What if a sensor goes offline?',
answer: 'Check that the sensor has battery power and is within range of the WellNuo Hub. You can see sensor status on the beneficiary detail page. If issues persist, contact support.',
},
];
const filteredFAQs = faqs.filter(
faq =>
faq.question.toLowerCase().includes(searchQuery.toLowerCase()) ||
faq.answer.toLowerCase().includes(searchQuery.toLowerCase())
);
return (
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
<PageHeader title="Help Center" />
<ScrollView showsVerticalScrollIndicator={false}>
{/* Search */}
<View style={styles.searchSection}>
<View style={styles.searchContainer}>
<Ionicons name="search" size={20} color={AppColors.textMuted} />
<TextInput
style={styles.searchInput}
placeholder="Search FAQ..."
placeholderTextColor={AppColors.textMuted}
value={searchQuery}
onChangeText={setSearchQuery}
/>
{searchQuery.length > 0 && (
<TouchableOpacity onPress={() => setSearchQuery('')}>
<Ionicons name="close-circle" size={20} color={AppColors.textMuted} />
</TouchableOpacity>
)}
</View>
</View>
{/* FAQs */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Frequently Asked Questions</Text>
<View style={styles.faqContainer}>
{filteredFAQs.length > 0 ? (
filteredFAQs.map((faq, index) => (
<View key={index}>
<FAQItem
question={faq.question}
answer={faq.answer}
isExpanded={expandedFAQ === index}
onToggle={() => setExpandedFAQ(expandedFAQ === index ? null : index)}
/>
{index < filteredFAQs.length - 1 && <View style={styles.faqDivider} />}
</View>
))
) : (
<View style={styles.noResults}>
<Ionicons name="search-outline" size={48} color={AppColors.textMuted} />
<Text style={styles.noResultsText}>No results found</Text>
<Text style={styles.noResultsHint}>Try different keywords</Text>
</View>
)}
</View>
</View>
{/* Contact Support */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Contact Support</Text>
<View style={styles.contactCard}>
<View style={styles.contactItem}>
<View style={styles.contactIcon}>
<Ionicons name="mail" size={22} color={AppColors.primary} />
</View>
<View style={styles.contactInfo}>
<Text style={styles.contactLabel}>Email</Text>
<TouchableOpacity onPress={() => Linking.openURL('mailto:support@wellnuo.com')}>
<Text style={styles.contactValue}>support@wellnuo.com</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.contactDivider} />
<View style={styles.contactItem}>
<View style={styles.contactIcon}>
<Ionicons name="call" size={22} color={AppColors.primary} />
</View>
<View style={styles.contactInfo}>
<Text style={styles.contactLabel}>Phone</Text>
<TouchableOpacity onPress={() => Linking.openURL('tel:+18005551234')}>
<Text style={styles.contactValue}>1-800-555-1234</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.contactDivider} />
<View style={styles.contactItem}>
<View style={styles.contactIcon}>
<Ionicons name="time" size={22} color={AppColors.primary} />
</View>
<View style={styles.contactInfo}>
<Text style={styles.contactLabel}>Hours</Text>
<Text style={styles.contactHours}>Mon - Fri: 9am - 6pm EST</Text>
</View>
</View>
</View>
</View>
{/* Spacer for bottom */}
<View style={{ height: Spacing.xl }} />
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: AppColors.background,
},
searchSection: {
padding: Spacing.lg,
},
searchContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: AppColors.surface,
borderRadius: BorderRadius.lg,
paddingHorizontal: Spacing.md,
paddingVertical: Spacing.sm,
},
searchInput: {
flex: 1,
marginLeft: Spacing.sm,
fontSize: FontSizes.base,
color: AppColors.textPrimary,
},
section: {
marginTop: Spacing.sm,
},
sectionTitle: {
fontSize: FontSizes.xs,
fontWeight: FontWeights.semibold,
color: AppColors.textMuted,
paddingHorizontal: Spacing.lg,
paddingVertical: Spacing.sm,
textTransform: 'uppercase',
letterSpacing: 0.5,
},
faqContainer: {
backgroundColor: AppColors.surface,
marginHorizontal: Spacing.lg,
borderRadius: BorderRadius.xl,
},
faqItem: {
paddingVertical: Spacing.md,
paddingHorizontal: Spacing.lg,
},
faqHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
faqQuestion: {
flex: 1,
fontSize: FontSizes.base,
fontWeight: FontWeights.medium,
color: AppColors.textPrimary,
marginRight: Spacing.sm,
},
faqAnswer: {
fontSize: FontSizes.sm,
color: AppColors.textSecondary,
marginTop: Spacing.sm,
lineHeight: 20,
},
faqDivider: {
height: 1,
backgroundColor: AppColors.borderLight,
marginHorizontal: Spacing.lg,
},
noResults: {
alignItems: 'center',
paddingVertical: Spacing.xl,
},
noResultsText: {
fontSize: FontSizes.base,
fontWeight: FontWeights.medium,
color: AppColors.textPrimary,
marginTop: Spacing.md,
},
noResultsHint: {
fontSize: FontSizes.sm,
color: AppColors.textMuted,
marginTop: Spacing.xs,
},
contactCard: {
backgroundColor: AppColors.surface,
marginHorizontal: Spacing.lg,
borderRadius: BorderRadius.xl,
padding: Spacing.md,
},
contactItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: Spacing.sm,
},
contactIcon: {
width: 44,
height: 44,
borderRadius: BorderRadius.lg,
backgroundColor: AppColors.primarySubtle,
justifyContent: 'center',
alignItems: 'center',
marginRight: Spacing.md,
},
contactInfo: {
flex: 1,
},
contactLabel: {
fontSize: FontSizes.xs,
color: AppColors.textMuted,
marginBottom: 2,
},
contactValue: {
fontSize: FontSizes.base,
fontWeight: FontWeights.medium,
color: AppColors.primary,
},
contactHours: {
fontSize: FontSizes.base,
color: AppColors.textPrimary,
},
contactDivider: {
height: 1,
backgroundColor: AppColors.borderLight,
marginLeft: 56,
},
});