@oxyhq/services
Version:
Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀
1,186 lines (1,167 loc) • 34.7 kB
JavaScript
"use strict";
import React, { useState, useRef, useCallback, useMemo } from 'react';
import { View, Text, TextInput, TouchableOpacity, StyleSheet, ActivityIndicator, Platform, KeyboardAvoidingView, ScrollView, Animated, StatusBar, Dimensions } from 'react-native';
import { useOxy } from '../context/OxyContext';
import { useThemeColors } from '../styles';
import { Ionicons } from '@expo/vector-icons';
import { toast } from '../../lib/sonner';
import { packageInfo } from '../../constants/version';
import { GroupedSection } from '../components';
import { useI18n } from '../hooks/useI18n';
// Types for better type safety
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
// Constants
const FEEDBACK_TYPES = [{
id: 'bug',
label: 'Bug Report',
icon: 'bug',
color: '#FF3B30',
description: 'Report a problem or issue'
}, {
id: 'feature',
label: 'Feature Request',
icon: 'bulb',
color: '#007AFF',
description: 'Suggest a new feature'
}, {
id: 'general',
label: 'General Feedback',
icon: 'chatbubble',
color: '#34C759',
description: 'Share your thoughts'
}, {
id: 'support',
label: 'Support Request',
icon: 'help-circle',
color: '#FF9500',
description: 'Get help with something'
}];
const PRIORITY_LEVELS = [{
id: 'low',
label: 'Low',
icon: 'arrow-down',
color: '#34C759'
}, {
id: 'medium',
label: 'Medium',
icon: 'remove',
color: '#FF9500'
}, {
id: 'high',
label: 'High',
icon: 'arrow-up',
color: '#FF3B30'
}, {
id: 'critical',
label: 'Critical',
icon: 'warning',
color: '#FF0000'
}];
const CATEGORIES = {
bug: ['UI/UX', 'Performance', 'Authentication', 'File Management', 'Billing', 'Other'],
feature: ['User Interface', 'File Management', 'Security', 'Performance', 'Integration', 'Other'],
general: ['User Experience', 'Design', 'Performance', 'Documentation', 'Other'],
support: ['Account Issues', 'Billing', 'Technical Problems', 'Feature Questions', 'Other']
};
// Styles factory function
const createStyles = (colors, theme) => StyleSheet.create({
container: {
flex: 1
},
fullBleed: {
width: '100%',
alignSelf: 'stretch'
},
scrollContent: {
flexGrow: 1,
paddingHorizontal: 24,
paddingTop: 40,
paddingBottom: 20
},
stepContainer: {
flex: 1,
justifyContent: 'flex-start',
alignItems: 'flex-start'
},
modernHeader: {
alignItems: 'flex-start',
width: '100%',
marginBottom: 24
},
modernTitle: {
fontFamily: Platform.OS === 'web' ? 'Phudu' : 'Phudu-Bold',
fontWeight: Platform.OS === 'web' ? 'bold' : undefined,
fontSize: 42,
lineHeight: 48,
marginBottom: 12,
textAlign: 'left',
letterSpacing: -1
},
modernSubtitle: {
fontSize: 18,
lineHeight: 24,
textAlign: 'left',
opacity: 0.8,
marginBottom: 24
},
stepTitle: {
fontFamily: Platform.OS === 'web' ? 'Phudu' : 'Phudu-Bold',
fontWeight: Platform.OS === 'web' ? 'bold' : undefined,
fontSize: 42,
lineHeight: 48,
marginBottom: 12,
textAlign: 'left',
letterSpacing: -1
},
inputContainer: {
width: '100%',
marginBottom: 24
},
premiumInputWrapper: {
flexDirection: 'row',
alignItems: 'center',
height: 56,
borderRadius: 16,
paddingHorizontal: 20,
borderWidth: 2,
backgroundColor: colors.inputBackground
},
textAreaWrapper: {
flexDirection: 'column',
alignItems: 'flex-start',
minHeight: 120,
borderRadius: 16,
paddingHorizontal: 20,
paddingVertical: 16,
borderWidth: 2,
backgroundColor: colors.inputBackground
},
inputIcon: {
marginRight: 12
},
inputContent: {
flex: 1
},
modernLabel: {
fontSize: 12,
fontWeight: '500',
marginBottom: 2
},
modernInput: {
flex: 1,
fontSize: 16,
height: '100%'
},
textArea: {
flex: 1,
fontSize: 16,
textAlignVertical: 'top',
minHeight: 80
},
typeGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 12,
marginBottom: 24
},
typeCard: {
width: (Dimensions.get('window').width - 72) / 2,
padding: 20,
borderRadius: 16,
borderWidth: 2,
alignItems: 'center',
justifyContent: 'center',
minHeight: 120
},
typeIcon: {
marginBottom: 12
},
typeLabel: {
fontSize: 16,
fontWeight: '600',
textAlign: 'center',
marginBottom: 4
},
typeDescription: {
fontSize: 12,
textAlign: 'center',
opacity: 0.8
},
priorityContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 24
},
priorityButton: {
flex: 1,
padding: 16,
borderRadius: 12,
borderWidth: 2,
alignItems: 'center',
marginHorizontal: 4
},
priorityLabel: {
fontSize: 12,
fontWeight: '600',
marginTop: 4
},
categoryContainer: {
marginBottom: 24
},
categoryButton: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 12,
paddingHorizontal: 16,
borderRadius: 12,
borderWidth: 1,
marginBottom: 8
},
categoryText: {
fontSize: 16,
marginLeft: 12
},
checkboxContainer: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 24
},
checkbox: {
width: 24,
height: 24,
borderRadius: 6,
borderWidth: 2,
marginRight: 12,
alignItems: 'center',
justifyContent: 'center'
},
checkboxText: {
fontSize: 16,
flex: 1
},
button: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 18,
paddingHorizontal: 32,
borderRadius: 16,
marginVertical: 8,
gap: 8,
width: '100%',
...Platform.select({
web: {
boxShadow: '0 4px 8px rgba(0,0,0,0.3)'
},
default: {
shadowOffset: {
width: 0,
height: 4
},
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 6
}
})
},
buttonText: {
color: '#FFFFFF',
fontSize: 16,
fontWeight: '600',
letterSpacing: 0.5
},
navigationButtons: {
flexDirection: 'row',
justifyContent: 'center',
marginTop: 16,
marginBottom: 8,
width: '100%',
gap: 8
},
navButton: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 6,
paddingHorizontal: 12,
gap: 6,
minWidth: 70,
borderWidth: 1,
...Platform.select({
web: {
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
},
default: {
shadowOffset: {
width: 0,
height: 2
},
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 2
}
})
},
backButton: {
backgroundColor: 'transparent',
borderTopLeftRadius: 35,
borderBottomLeftRadius: 35,
borderTopRightRadius: 12,
borderBottomRightRadius: 12
},
nextButton: {
backgroundColor: 'transparent',
borderTopRightRadius: 35,
borderBottomRightRadius: 35,
borderTopLeftRadius: 12,
borderBottomLeftRadius: 12
},
navButtonText: {
fontSize: 13,
fontWeight: '500'
},
progressContainer: {
flexDirection: 'row',
justifyContent: 'center',
marginBottom: 20,
marginTop: 8
},
progressDot: {
height: 10,
width: 10,
borderRadius: 5,
marginHorizontal: 6,
borderWidth: 2,
borderColor: '#fff',
...Platform.select({
web: {
boxShadow: '0 1px 2px rgba(0,0,0,0.08)'
},
default: {
shadowColor: colors.primary,
shadowOpacity: 0.08,
shadowOffset: {
width: 0,
height: 1
},
shadowRadius: 2,
elevation: 1
}
})
},
summaryContainer: {
padding: 0,
marginBottom: 24,
width: '100%'
},
summaryRow: {
flexDirection: 'row',
marginBottom: 10
},
summaryLabel: {
fontSize: 15,
width: 90
},
summaryValue: {
fontSize: 15,
fontWeight: '600',
flex: 1
},
successContainer: {
alignItems: 'center',
justifyContent: 'center',
padding: 40
},
successIcon: {
marginBottom: 24
},
successTitle: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 12,
textAlign: 'center'
},
successMessage: {
fontSize: 16,
textAlign: 'center',
opacity: 0.8,
marginBottom: 24
}
});
// Custom hooks for better separation of concerns
const useFeedbackForm = () => {
const [feedbackData, setFeedbackData] = useState({
type: 'general',
title: '',
description: '',
priority: 'medium',
category: '',
contactEmail: '',
systemInfo: true
});
const [feedbackState, setFeedbackState] = useState({
status: 'idle',
message: ''
});
const updateField = useCallback((field, value) => {
setFeedbackData(prev => ({
...prev,
[field]: value
}));
}, []);
const resetForm = useCallback(() => {
setFeedbackData({
type: 'general',
title: '',
description: '',
priority: 'medium',
category: '',
contactEmail: '',
systemInfo: true
});
setFeedbackState({
status: 'idle',
message: ''
});
}, []);
return {
feedbackData,
feedbackState,
setFeedbackState,
updateField,
resetForm
};
};
// Reusable components
const FormInput = /*#__PURE__*/React.memo(({
icon,
label,
value,
onChangeText,
placeholder,
multiline = false,
numberOfLines = 1,
testID,
colors,
styles,
borderColor
}) => /*#__PURE__*/_jsx(View, {
style: styles.inputContainer,
children: /*#__PURE__*/_jsxs(View, {
style: [multiline ? styles.textAreaWrapper : styles.premiumInputWrapper, {
borderColor: borderColor || colors.border,
backgroundColor: colors.inputBackground,
shadowColor: colors.primary,
shadowOffset: {
width: 0,
height: 4
},
shadowOpacity: 0.1,
shadowRadius: 12,
elevation: 3
}],
children: [!multiline && /*#__PURE__*/_jsx(Ionicons, {
name: icon,
size: 22,
color: colors.secondaryText,
style: styles.inputIcon
}), /*#__PURE__*/_jsxs(View, {
style: styles.inputContent,
children: [/*#__PURE__*/_jsx(Text, {
style: [styles.modernLabel, {
color: colors.secondaryText
}],
children: label
}), /*#__PURE__*/_jsx(TextInput, {
style: [multiline ? styles.textArea : styles.modernInput, {
color: colors.text
}],
value: value,
onChangeText: onChangeText,
placeholder: placeholder,
placeholderTextColor: colors.secondaryText + '60',
multiline: multiline,
numberOfLines: multiline ? numberOfLines : undefined,
testID: testID
})]
})]
})
}));
const ProgressIndicator = /*#__PURE__*/React.memo(({
currentStep,
totalSteps,
colors,
styles
}) => /*#__PURE__*/_jsx(View, {
style: styles.progressContainer,
children: Array.from({
length: totalSteps
}, (_, index) => /*#__PURE__*/_jsx(View, {
style: [styles.progressDot, currentStep === index ? {
backgroundColor: colors.primary,
width: 24
} : {
backgroundColor: colors.border
}]
}, index))
}));
// Main component
const FeedbackScreen = ({
navigate,
goBack,
onClose,
theme
}) => {
const {
user,
oxyServices
} = useOxy();
const colors = useThemeColors(theme);
const {
t
} = useI18n();
// Form state
const {
feedbackData,
feedbackState,
setFeedbackState,
updateField,
resetForm
} = useFeedbackForm();
// UI state
const [currentStep, setCurrentStep] = useState(0);
const [errorMessage, setErrorMessage] = useState('');
// Animation refs
const fadeAnim = useRef(new Animated.Value(1)).current;
const slideAnim = useRef(new Animated.Value(0)).current;
// Memoized styles
const styles = useMemo(() => createStyles(colors, theme), [colors, theme]);
// Animation functions
const animateTransition = useCallback(nextStep => {
Animated.timing(fadeAnim, {
toValue: 0,
duration: 250,
useNativeDriver: Platform.OS !== 'web'
}).start(() => {
setCurrentStep(nextStep);
slideAnim.setValue(-100);
Animated.parallel([Animated.timing(fadeAnim, {
toValue: 1,
duration: 250,
useNativeDriver: Platform.OS !== 'web'
}), Animated.timing(slideAnim, {
toValue: 0,
duration: 300,
useNativeDriver: Platform.OS !== 'web'
})]).start();
});
}, [fadeAnim, slideAnim]);
const nextStep = useCallback(() => {
if (currentStep < 3) {
animateTransition(currentStep + 1);
}
}, [currentStep, animateTransition]);
const prevStep = useCallback(() => {
if (currentStep > 0) {
animateTransition(currentStep - 1);
}
}, [currentStep, animateTransition]);
// Form validation helpers
const isTypeStepValid = useCallback(() => {
return feedbackData.type && feedbackData.category;
}, [feedbackData.type, feedbackData.category]);
const isDetailsStepValid = useCallback(() => {
return feedbackData.title.trim() && feedbackData.description.trim();
}, [feedbackData.title, feedbackData.description]);
const isContactStepValid = useCallback(() => {
return feedbackData.contactEmail.trim() || user?.email;
}, [feedbackData.contactEmail, user?.email]);
// Submit feedback handler
const handleSubmitFeedback = useCallback(async () => {
if (!isTypeStepValid() || !isDetailsStepValid() || !isContactStepValid()) {
toast.error(t('feedback.toasts.fillRequired') || 'Please fill in all required fields');
return;
}
try {
setFeedbackState({
status: 'submitting',
message: ''
});
setErrorMessage('');
// Prepare feedback data
const feedbackPayload = {
type: feedbackData.type,
title: feedbackData.title,
description: feedbackData.description,
priority: feedbackData.priority,
category: feedbackData.category,
contactEmail: feedbackData.contactEmail || user?.email,
systemInfo: feedbackData.systemInfo ? {
platform: Platform.OS,
version: Platform.Version?.toString() || 'Unknown',
appVersion: packageInfo.version,
userId: user?.id,
username: user?.username,
timestamp: new Date().toISOString()
} : undefined
};
// For now, we'll simulate the API call
// In a real implementation, you would call oxyServices.submitFeedback(feedbackPayload)
await new Promise(resolve => setTimeout(resolve, 2000)); // Simulate API call
setFeedbackState({
status: 'success',
message: t('feedback.toasts.submitSuccess') || 'Feedback submitted successfully!'
});
toast.success(t('feedback.toasts.thanks') || 'Thank you for your feedback!');
// Reset form after success
setTimeout(() => {
resetForm();
setCurrentStep(0);
}, 3000);
} catch (error) {
setFeedbackState({
status: 'error',
message: error.message || t('feedback.toasts.submitFailed') || 'Failed to submit feedback'
});
toast.error(error.message || t('feedback.toasts.submitFailed') || 'Failed to submit feedback');
}
}, [feedbackData, user, isTypeStepValid, isDetailsStepValid, isContactStepValid, resetForm]);
// Step components
// Memoized grouped section items
const feedbackTypeItems = useMemo(() => FEEDBACK_TYPES.map(type => ({
id: type.id,
icon: type.icon,
iconColor: type.color,
title: type.label,
subtitle: type.description,
onPress: () => {
updateField('type', type.id);
updateField('category', '');
},
selected: feedbackData.type === type.id,
showChevron: false,
multiRow: true,
dense: true
})), [feedbackData.type, updateField]);
const categoryItems = useMemo(() => feedbackData.type ? (CATEGORIES[feedbackData.type] || []).map(cat => ({
id: cat,
icon: feedbackData.category === cat ? 'checkmark-circle' : 'ellipse-outline',
iconColor: feedbackData.category === cat ? colors.primary : colors.secondaryText,
title: cat,
onPress: () => updateField('category', cat),
selected: feedbackData.category === cat,
showChevron: false,
dense: true
})) : [], [feedbackData.type, feedbackData.category, colors.primary, colors.secondaryText, updateField]);
const priorityItems = useMemo(() => PRIORITY_LEVELS.map(p => ({
id: p.id,
icon: p.icon,
iconColor: p.color,
title: p.label,
onPress: () => updateField('priority', p.id),
selected: feedbackData.priority === p.id,
showChevron: false,
dense: true
})), [feedbackData.priority, updateField]);
const renderTypeStep = useCallback(() => /*#__PURE__*/_jsxs(Animated.View, {
style: [styles.stepContainer, {
opacity: fadeAnim,
transform: [{
translateX: slideAnim
}]
}],
children: [/*#__PURE__*/_jsxs(View, {
style: styles.modernHeader,
children: [/*#__PURE__*/_jsx(Text, {
style: [styles.stepTitle, {
color: colors.text
}],
children: t('feedback.type.title') || 'What type of feedback?'
}), /*#__PURE__*/_jsx(Text, {
style: [styles.modernSubtitle, {
color: colors.secondaryText
}],
children: t('feedback.type.subtitle') || 'Choose the category that best describes your feedback'
})]
}), /*#__PURE__*/_jsx(View, {
style: styles.fullBleed,
children: /*#__PURE__*/_jsx(GroupedSection, {
items: feedbackTypeItems,
theme: theme
})
}), feedbackData.type && /*#__PURE__*/_jsxs(View, {
style: styles.categoryContainer,
children: [/*#__PURE__*/_jsx(Text, {
style: [styles.modernLabel, {
color: colors.secondaryText,
marginBottom: 8
}],
children: t('feedback.category.label') || 'Category'
}), /*#__PURE__*/_jsx(View, {
style: styles.fullBleed,
children: /*#__PURE__*/_jsx(GroupedSection, {
items: categoryItems,
theme: theme
})
})]
}), /*#__PURE__*/_jsxs(View, {
style: styles.navigationButtons,
children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
style: [styles.navButton, {
backgroundColor: 'transparent',
borderColor: colors.border,
shadowColor: colors.border,
borderTopLeftRadius: 35,
borderBottomLeftRadius: 35,
borderTopRightRadius: 35,
borderBottomRightRadius: 35
}],
onPress: goBack,
children: [/*#__PURE__*/_jsx(Ionicons, {
name: "arrow-back",
size: 16,
color: colors.text
}), /*#__PURE__*/_jsx(Text, {
style: [styles.navButtonText, {
color: colors.text
}],
children: t('common.actions.back') || 'Back'
})]
}), /*#__PURE__*/_jsxs(TouchableOpacity, {
style: [styles.navButton, {
backgroundColor: colors.primary,
borderColor: colors.primary,
shadowColor: colors.primary,
borderTopLeftRadius: 35,
borderBottomLeftRadius: 35,
borderTopRightRadius: 35,
borderBottomRightRadius: 35
}],
onPress: nextStep,
disabled: !isTypeStepValid(),
children: [/*#__PURE__*/_jsx(Text, {
style: [styles.navButtonText, {
color: '#FFFFFF'
}],
children: t('common.actions.next') || 'Next'
}), /*#__PURE__*/_jsx(Ionicons, {
name: "arrow-forward",
size: 16,
color: "#FFFFFF"
})]
})]
})]
}), [fadeAnim, slideAnim, colors, feedbackData, feedbackTypeItems, categoryItems, updateField, goBack, nextStep, isTypeStepValid, styles, theme]);
const renderDetailsStep = useCallback(() => /*#__PURE__*/_jsxs(Animated.View, {
style: [styles.stepContainer, {
opacity: fadeAnim,
transform: [{
translateX: slideAnim
}]
}],
children: [/*#__PURE__*/_jsxs(View, {
style: styles.modernHeader,
children: [/*#__PURE__*/_jsx(Text, {
style: [styles.stepTitle, {
color: colors.text
}],
children: t('feedback.details.title') || 'Tell us more'
}), /*#__PURE__*/_jsx(Text, {
style: [styles.modernSubtitle, {
color: colors.secondaryText
}],
children: t('feedback.details.subtitle') || 'Provide details about your feedback'
})]
}), /*#__PURE__*/_jsx(FormInput, {
icon: "create-outline",
label: t('feedback.fields.title.label') || 'Title',
value: feedbackData.title,
onChangeText: text => {
updateField('title', text);
setErrorMessage('');
},
placeholder: t('feedback.fields.title.placeholder') || 'Brief summary of your feedback',
testID: "feedback-title-input",
colors: colors,
styles: styles
}), /*#__PURE__*/_jsx(FormInput, {
icon: "document-text-outline",
label: t('feedback.fields.description.label') || 'Description',
value: feedbackData.description,
onChangeText: text => {
updateField('description', text);
setErrorMessage('');
},
placeholder: t('feedback.fields.description.placeholder') || 'Please provide detailed information...',
multiline: true,
numberOfLines: 6,
testID: "feedback-description-input",
colors: colors,
styles: styles
}), /*#__PURE__*/_jsxs(View, {
style: {
marginBottom: 24
},
children: [/*#__PURE__*/_jsx(Text, {
style: [styles.modernLabel, {
color: colors.secondaryText,
marginBottom: 8
}],
children: t('feedback.priority.label') || 'Priority Level'
}), /*#__PURE__*/_jsx(View, {
style: styles.fullBleed,
children: /*#__PURE__*/_jsx(GroupedSection, {
items: priorityItems,
theme: theme
})
})]
}), /*#__PURE__*/_jsxs(View, {
style: styles.navigationButtons,
children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
style: [styles.navButton, styles.backButton, {
borderColor: colors.border,
shadowColor: colors.border
}],
onPress: prevStep,
children: [/*#__PURE__*/_jsx(Ionicons, {
name: "arrow-back",
size: 16,
color: colors.text
}), /*#__PURE__*/_jsx(Text, {
style: [styles.navButtonText, {
color: colors.text
}],
children: t('common.actions.back') || 'Back'
})]
}), /*#__PURE__*/_jsxs(TouchableOpacity, {
style: [styles.navButton, styles.nextButton, {
backgroundColor: colors.primary,
borderColor: colors.primary,
shadowColor: colors.primary
}],
onPress: nextStep,
disabled: !isDetailsStepValid(),
children: [/*#__PURE__*/_jsx(Text, {
style: [styles.navButtonText, {
color: '#FFFFFF'
}],
children: t('common.actions.next') || 'Next'
}), /*#__PURE__*/_jsx(Ionicons, {
name: "arrow-forward",
size: 16,
color: "#FFFFFF"
})]
})]
})]
}), [fadeAnim, slideAnim, colors, feedbackData, updateField, setErrorMessage, prevStep, nextStep, isDetailsStepValid, styles, priorityItems, theme]);
const renderContactStep = useCallback(() => /*#__PURE__*/_jsxs(Animated.View, {
style: [styles.stepContainer, {
opacity: fadeAnim,
transform: [{
translateX: slideAnim
}]
}],
children: [/*#__PURE__*/_jsxs(View, {
style: styles.modernHeader,
children: [/*#__PURE__*/_jsx(Text, {
style: [styles.stepTitle, {
color: colors.text
}],
children: t('feedback.contact.title') || 'Contact Information'
}), /*#__PURE__*/_jsx(Text, {
style: [styles.modernSubtitle, {
color: colors.secondaryText
}],
children: t('feedback.contact.subtitle') || 'Help us get back to you'
})]
}), /*#__PURE__*/_jsx(FormInput, {
icon: "mail-outline",
label: t('feedback.fields.email.label') || 'Email Address',
value: feedbackData.contactEmail,
onChangeText: text => {
updateField('contactEmail', text);
setErrorMessage('');
},
placeholder: user?.email || t('feedback.fields.email.placeholder') || 'Enter your email address',
testID: "feedback-email-input",
colors: colors,
styles: styles
}), /*#__PURE__*/_jsxs(View, {
style: styles.checkboxContainer,
children: [/*#__PURE__*/_jsx(TouchableOpacity, {
style: [styles.checkbox, {
borderColor: feedbackData.systemInfo ? colors.primary : colors.border,
backgroundColor: feedbackData.systemInfo ? colors.primary : 'transparent'
}],
onPress: () => updateField('systemInfo', !feedbackData.systemInfo),
children: feedbackData.systemInfo && /*#__PURE__*/_jsx(Ionicons, {
name: "checkmark",
size: 16,
color: "#FFFFFF"
})
}), /*#__PURE__*/_jsx(Text, {
style: [styles.checkboxText, {
color: colors.text
}],
children: t('feedback.contact.includeSystemInfo') || 'Include system information to help us better understand your issue'
})]
}), /*#__PURE__*/_jsxs(View, {
style: styles.navigationButtons,
children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
style: [styles.navButton, styles.backButton, {
borderColor: colors.border,
shadowColor: colors.border
}],
onPress: prevStep,
children: [/*#__PURE__*/_jsx(Ionicons, {
name: "arrow-back",
size: 16,
color: colors.text
}), /*#__PURE__*/_jsx(Text, {
style: [styles.navButtonText, {
color: colors.text
}],
children: "Back"
})]
}), /*#__PURE__*/_jsxs(TouchableOpacity, {
style: [styles.navButton, styles.nextButton, {
backgroundColor: colors.primary,
borderColor: colors.primary,
shadowColor: colors.primary
}],
onPress: nextStep,
disabled: !isContactStepValid(),
children: [/*#__PURE__*/_jsx(Text, {
style: [styles.navButtonText, {
color: '#FFFFFF'
}],
children: "Next"
}), /*#__PURE__*/_jsx(Ionicons, {
name: "arrow-forward",
size: 16,
color: "#FFFFFF"
})]
})]
})]
}), [fadeAnim, slideAnim, colors, feedbackData, user, updateField, setErrorMessage, prevStep, nextStep, isContactStepValid, styles]);
const renderSummaryStep = useCallback(() => /*#__PURE__*/_jsxs(Animated.View, {
style: [styles.stepContainer, {
opacity: fadeAnim,
transform: [{
translateX: slideAnim
}]
}],
children: [/*#__PURE__*/_jsxs(View, {
style: styles.modernHeader,
children: [/*#__PURE__*/_jsx(Text, {
style: [styles.stepTitle, {
color: colors.text
}],
children: t('feedback.summary.title') || 'Summary'
}), /*#__PURE__*/_jsx(Text, {
style: [styles.modernSubtitle, {
color: colors.secondaryText
}],
children: t('feedback.summary.subtitle') || 'Please review your feedback before submitting'
})]
}), /*#__PURE__*/_jsxs(View, {
style: styles.summaryContainer,
children: [/*#__PURE__*/_jsxs(View, {
style: styles.summaryRow,
children: [/*#__PURE__*/_jsx(Text, {
style: [styles.summaryLabel, {
color: colors.secondaryText
}],
children: t('feedback.summary.type') || 'Type:'
}), /*#__PURE__*/_jsx(Text, {
style: [styles.summaryValue, {
color: colors.text
}],
children: FEEDBACK_TYPES.find(t => t.id === feedbackData.type)?.label
})]
}), /*#__PURE__*/_jsxs(View, {
style: styles.summaryRow,
children: [/*#__PURE__*/_jsx(Text, {
style: [styles.summaryLabel, {
color: colors.secondaryText
}],
children: t('feedback.summary.category') || 'Category:'
}), /*#__PURE__*/_jsx(Text, {
style: [styles.summaryValue, {
color: colors.text
}],
children: feedbackData.category
})]
}), /*#__PURE__*/_jsxs(View, {
style: styles.summaryRow,
children: [/*#__PURE__*/_jsx(Text, {
style: [styles.summaryLabel, {
color: colors.secondaryText
}],
children: t('feedback.summary.priority') || 'Priority:'
}), /*#__PURE__*/_jsx(Text, {
style: [styles.summaryValue, {
color: colors.text
}],
children: PRIORITY_LEVELS.find(p => p.id === feedbackData.priority)?.label
})]
}), /*#__PURE__*/_jsxs(View, {
style: styles.summaryRow,
children: [/*#__PURE__*/_jsx(Text, {
style: [styles.summaryLabel, {
color: colors.secondaryText
}],
children: t('feedback.summary.titleLabel') || 'Title:'
}), /*#__PURE__*/_jsx(Text, {
style: [styles.summaryValue, {
color: colors.text
}],
children: feedbackData.title
})]
}), /*#__PURE__*/_jsxs(View, {
style: styles.summaryRow,
children: [/*#__PURE__*/_jsx(Text, {
style: [styles.summaryLabel, {
color: colors.secondaryText
}],
children: t('feedback.summary.contact') || 'Contact:'
}), /*#__PURE__*/_jsx(Text, {
style: [styles.summaryValue, {
color: colors.text
}],
children: feedbackData.contactEmail || user?.email
})]
})]
}), /*#__PURE__*/_jsx(TouchableOpacity, {
style: [styles.button, {
backgroundColor: colors.primary
}],
onPress: handleSubmitFeedback,
disabled: feedbackState.status === 'submitting',
testID: "submit-feedback-button",
children: feedbackState.status === 'submitting' ? /*#__PURE__*/_jsx(ActivityIndicator, {
color: "#FFFFFF",
size: "small"
}) : /*#__PURE__*/_jsxs(_Fragment, {
children: [/*#__PURE__*/_jsx(Text, {
style: styles.buttonText,
children: t('feedback.actions.submit') || 'Submit Feedback'
}), /*#__PURE__*/_jsx(Ionicons, {
name: "send",
size: 20,
color: "#FFFFFF"
})]
})
}), /*#__PURE__*/_jsx(View, {
style: styles.navigationButtons,
children: /*#__PURE__*/_jsxs(TouchableOpacity, {
style: [styles.navButton, {
backgroundColor: 'transparent',
borderColor: colors.border,
shadowColor: colors.border,
borderTopLeftRadius: 35,
borderBottomLeftRadius: 35,
borderTopRightRadius: 35,
borderBottomRightRadius: 35
}],
onPress: prevStep,
children: [/*#__PURE__*/_jsx(Ionicons, {
name: "arrow-back",
size: 16,
color: colors.text
}), /*#__PURE__*/_jsx(Text, {
style: [styles.navButtonText, {
color: colors.text
}],
children: t('common.actions.back') || 'Back'
})]
})
})]
}), [fadeAnim, slideAnim, colors, feedbackData, user, feedbackState.status, handleSubmitFeedback, prevStep, styles]);
const renderSuccessStep = useCallback(() => /*#__PURE__*/_jsx(Animated.View, {
style: [styles.stepContainer, {
opacity: fadeAnim,
transform: [{
translateX: slideAnim
}]
}],
children: /*#__PURE__*/_jsxs(View, {
style: styles.successContainer,
children: [/*#__PURE__*/_jsx(View, {
style: [styles.successIcon, {
backgroundColor: colors.success + '20',
padding: 24,
borderRadius: 50
}],
children: /*#__PURE__*/_jsx(Ionicons, {
name: "checkmark-circle",
size: 48,
color: colors.success
})
}), /*#__PURE__*/_jsx(Text, {
style: [styles.successTitle, {
color: colors.text
}],
children: t('feedback.success.thanks') || 'Thank You!'
}), /*#__PURE__*/_jsx(Text, {
style: [styles.successMessage, {
color: colors.secondaryText
}],
children: t('feedback.success.message') || "Your feedback has been submitted successfully. We'll review it and get back to you soon."
}), /*#__PURE__*/_jsx(TouchableOpacity, {
style: [styles.button, {
backgroundColor: colors.primary
}],
onPress: () => {
resetForm();
setCurrentStep(0);
},
children: /*#__PURE__*/_jsx(Text, {
style: styles.buttonText,
children: t('feedback.actions.submitAnother') || 'Submit Another'
})
})]
})
}), [fadeAnim, slideAnim, colors, resetForm, styles]);
// Render current step
const renderCurrentStep = useCallback(() => {
if (feedbackState.status === 'success') {
return renderSuccessStep();
}
switch (currentStep) {
case 0:
return renderTypeStep();
case 1:
return renderDetailsStep();
case 2:
return renderContactStep();
case 3:
return renderSummaryStep();
default:
return renderTypeStep();
}
}, [currentStep, feedbackState.status, renderTypeStep, renderDetailsStep, renderContactStep, renderSummaryStep, renderSuccessStep]);
return /*#__PURE__*/_jsxs(KeyboardAvoidingView, {
style: [styles.container, {
backgroundColor: theme === 'dark' ? colors.background : '#F7F9FC'
}],
behavior: Platform.OS === 'ios' ? 'padding' : 'height',
children: [/*#__PURE__*/_jsx(StatusBar, {
barStyle: theme === 'dark' ? 'light-content' : 'dark-content',
backgroundColor: theme === 'dark' ? colors.background : '#F7F9FC'
}), /*#__PURE__*/_jsxs(ScrollView, {
contentContainerStyle: [styles.scrollContent, {
backgroundColor: 'transparent'
}],
showsVerticalScrollIndicator: false,
keyboardShouldPersistTaps: "handled",
children: [feedbackState.status !== 'success' && /*#__PURE__*/_jsx(ProgressIndicator, {
currentStep: currentStep,
totalSteps: 4,
colors: colors,
styles: styles
}), renderCurrentStep()]
})]
});
};
export default FeedbackScreen;
//# sourceMappingURL=FeedbackScreen.js.map