326 lines
12 KiB
TypeScript
326 lines
12 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
||
import {
|
||
View,
|
||
Text,
|
||
ScrollView,
|
||
TouchableOpacity,
|
||
FlatList,
|
||
Modal,
|
||
TextInput,
|
||
ActivityIndicator,
|
||
Alert,
|
||
} from 'react-native';
|
||
import { useNavigation, useIsFocused } from '@react-navigation/native';
|
||
import { StackNavigationProp } from '@react-navigation/stack';
|
||
import { getStyles } from './helpSupportScreen.styles';
|
||
import { Header } from '@components';
|
||
import { useAppTheme } from '@theme';
|
||
import { AppStackParamList } from '../../../navigation/appStack';
|
||
import { useCustomerSupport } from '../../../hooks/useCustomerSupport';
|
||
import { TicketCategory, TicketPriority } from '@interfaces';
|
||
import { formatDate } from '../../../utils/helper';
|
||
|
||
type HelpSupportNavProp = StackNavigationProp<AppStackParamList, 'HelpSupportScreen'>;
|
||
|
||
const FAQS = [
|
||
{ q: 'How do I track my order?', a: 'Go to Orders tab and tap on your active order to see tracking.' },
|
||
{ q: 'What is the delivery time?', a: 'Standard delivery takes 25-30 min, Express takes 10-15 min.' },
|
||
{ q: 'How do I cancel an order?', a: 'Contact support to cancel an order before it is dispatched.' },
|
||
{ q: 'What payment methods are accepted?', a: 'UPI, Credit/Debit Card, Wallet, and Cash on Delivery.' },
|
||
];
|
||
|
||
const TICKET_CATEGORIES: { value: TicketCategory; label: string }[] = [
|
||
{ value: 'ORDER', label: 'Order' },
|
||
{ value: 'PAYMENT', label: 'Payment' },
|
||
{ value: 'PAYOUT', label: 'Payout' },
|
||
{ value: 'TECHNICAL_ISSUE', label: 'Technical Issue' },
|
||
{ value: 'ACCOUNT', label: 'Account' },
|
||
{ value: 'OTHER', label: 'Other' },
|
||
];
|
||
|
||
export const HelpSupportScreen: React.FC = () => {
|
||
const { colors } = useAppTheme();
|
||
const styles = getStyles(colors);
|
||
const navigation = useNavigation<HelpSupportNavProp>();
|
||
const isFocused = useIsFocused();
|
||
|
||
// Tab State
|
||
const [activeTab, setActiveTab] = useState<'faq' | 'tickets'>('faq');
|
||
|
||
// Custom Support Hook
|
||
const { tickets, loading, fetchTickets, createTicket } = useCustomerSupport();
|
||
|
||
// Create Ticket Modal State
|
||
const [createModalVisible, setCreateModalVisible] = useState(false);
|
||
const [categorySelectVisible, setCategorySelectVisible] = useState(false);
|
||
|
||
const [category, setCategory] = useState<TicketCategory>('ORDER');
|
||
const [priority] = useState<TicketPriority>('MEDIUM');
|
||
const [subject, setSubject] = useState('');
|
||
const [description, setDescription] = useState('');
|
||
|
||
useEffect(() => {
|
||
if (isFocused && activeTab === 'tickets') {
|
||
fetchTickets();
|
||
}
|
||
}, [isFocused, activeTab, fetchTickets]);
|
||
|
||
const handleCreateTicketSubmit = async () => {
|
||
if (!subject.trim()) {
|
||
Alert.alert('Error', 'Please enter a subject.');
|
||
return;
|
||
}
|
||
if (!description.trim()) {
|
||
Alert.alert('Error', 'Please enter ticket details.');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const newTicket = await createTicket({
|
||
category,
|
||
priority,
|
||
subject: subject.trim(),
|
||
description: description.trim(),
|
||
});
|
||
setCreateModalVisible(false);
|
||
setSubject('');
|
||
setDescription('');
|
||
Alert.alert('Success', 'Support Ticket created successfully!');
|
||
// Navigate to chat
|
||
navigation.navigate('SupportChatScreen', { ticketId: newTicket.id });
|
||
} catch (error) {
|
||
Alert.alert('Error', 'Failed to create support ticket. Please try again.');
|
||
}
|
||
};
|
||
|
||
const getStatusColor = (status: string) => {
|
||
switch (status) {
|
||
case 'OPEN':
|
||
return { bg: '#E3F2FD', text: '#1E88E5' }; // Blue
|
||
case 'IN_PROGRESS':
|
||
return { bg: '#FFF3E0', text: '#FB8C00' }; // Amber
|
||
case 'RESOLVED':
|
||
return { bg: '#E8F5E9', text: '#43A047' }; // Green
|
||
case 'CLOSED':
|
||
return { bg: '#ECEFF1', text: '#546E7A' }; // Grey
|
||
default:
|
||
return { bg: '#F5F5F5', text: '#9E9E9E' };
|
||
}
|
||
};
|
||
|
||
const renderFaqTab = () => (
|
||
<ScrollView contentContainerStyle={styles.content} showsVerticalScrollIndicator={false}>
|
||
<Text style={styles.sectionTitle}>Frequently Asked Questions</Text>
|
||
{FAQS.map((faq, index) => (
|
||
<View key={index} style={styles.faqItem}>
|
||
<Text style={styles.faqQuestion}>{faq.q}</Text>
|
||
<Text style={styles.faqAnswer}>{faq.a}</Text>
|
||
</View>
|
||
))}
|
||
|
||
<Text style={styles.sectionTitle}>Contact Support</Text>
|
||
<TouchableOpacity style={styles.supportCard} activeOpacity={0.7}>
|
||
<Text style={styles.supportIcon}>📞</Text>
|
||
<View>
|
||
<Text style={styles.supportLabel}>Call us</Text>
|
||
<Text style={styles.supportValue}>+91 1800 123 4567</Text>
|
||
</View>
|
||
</TouchableOpacity>
|
||
<TouchableOpacity style={styles.supportCard} activeOpacity={0.7}>
|
||
<Text style={styles.supportIcon}>✉️</Text>
|
||
<View>
|
||
<Text style={styles.supportLabel}>Email us</Text>
|
||
<Text style={styles.supportValue}>support@sgdelivery.com</Text>
|
||
</View>
|
||
</TouchableOpacity>
|
||
</ScrollView>
|
||
);
|
||
|
||
const renderTicketsTab = () => (
|
||
<View style={[styles.container, { padding: 16 }]}>
|
||
{loading && tickets.length === 0 ? (
|
||
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
||
<ActivityIndicator size="large" color={colors.primary} />
|
||
<Text style={{ marginTop: 8, color: colors.textSecondary }}>Fetching tickets...</Text>
|
||
</View>
|
||
) : (
|
||
<FlatList
|
||
data={tickets}
|
||
keyExtractor={(item) => item.id}
|
||
showsVerticalScrollIndicator={false}
|
||
ListEmptyComponent={
|
||
<View style={{ paddingVertical: 40, alignItems: 'center' }}>
|
||
<Text style={{ fontSize: 16, color: colors.textSecondary, marginBottom: 12 }}>
|
||
No support tickets found
|
||
</Text>
|
||
<Text style={{ fontSize: 12, color: colors.textSecondary, textAlign: 'center' }}>
|
||
Need help with your account or order? Create a ticket below!
|
||
</Text>
|
||
</View>
|
||
}
|
||
renderItem={({ item }) => {
|
||
const statusStyle = getStatusColor(item.status);
|
||
return (
|
||
<TouchableOpacity
|
||
style={styles.ticketCard}
|
||
activeOpacity={0.7}
|
||
onPress={() => navigation.navigate('SupportChatScreen', { ticketId: item.id })}
|
||
>
|
||
<View style={styles.ticketHeader}>
|
||
<Text style={styles.ticketNumber}>Ticket #{item.ticketNumber}</Text>
|
||
<Text style={styles.ticketDate}>{formatDate(item.createdAt)}</Text>
|
||
</View>
|
||
<Text style={styles.ticketSubjectText} numberOfLines={1}>
|
||
{item.subject}
|
||
</Text>
|
||
<View style={styles.ticketFooter}>
|
||
<View style={styles.categoryBadge}>
|
||
<Text style={styles.categoryText}>{item.category.replace('_', ' ')}</Text>
|
||
</View>
|
||
<View style={[styles.statusBadge, { backgroundColor: statusStyle.bg }]}>
|
||
<Text style={[styles.statusText, { color: statusStyle.text }]}>
|
||
{item.status.replace('_', ' ')}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
</TouchableOpacity>
|
||
);
|
||
}}
|
||
/>
|
||
)}
|
||
|
||
<TouchableOpacity
|
||
style={styles.createTicketBtn}
|
||
onPress={() => setCreateModalVisible(true)}
|
||
activeOpacity={0.8}
|
||
>
|
||
<Text style={styles.createTicketBtnText}>+ Create Support Ticket</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
);
|
||
|
||
return (
|
||
<View style={styles.container}>
|
||
<Header title="Help & Support" onBack={() => navigation.goBack()} />
|
||
|
||
<View style={{ paddingHorizontal: 16, paddingTop: 16 }}>
|
||
<View style={styles.tabContainer}>
|
||
<TouchableOpacity
|
||
style={[styles.tabButton, activeTab === 'faq' && styles.activeTabButton]}
|
||
onPress={() => setActiveTab('faq')}
|
||
>
|
||
<Text style={[styles.tabText, activeTab === 'faq' && styles.activeTabText]}>FAQs</Text>
|
||
</TouchableOpacity>
|
||
<TouchableOpacity
|
||
style={[styles.tabButton, activeTab === 'tickets' && styles.activeTabButton]}
|
||
onPress={() => setActiveTab('tickets')}
|
||
>
|
||
<Text style={[styles.tabText, activeTab === 'tickets' && styles.activeTabText]}>
|
||
My Support Tickets
|
||
</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
</View>
|
||
|
||
{activeTab === 'faq' ? renderFaqTab() : renderTicketsTab()}
|
||
|
||
{/* Create Ticket Modal */}
|
||
<Modal
|
||
visible={createModalVisible}
|
||
transparent
|
||
animationType="slide"
|
||
onRequestClose={() => setCreateModalVisible(false)}
|
||
>
|
||
<View style={styles.modalOverlay}>
|
||
<View style={styles.modalContent}>
|
||
<Text style={styles.modalTitle}>Create Support Ticket</Text>
|
||
|
||
<ScrollView showsVerticalScrollIndicator={false}>
|
||
<Text style={styles.label}>Category</Text>
|
||
<TouchableOpacity
|
||
style={styles.categorySelect}
|
||
onPress={() => setCategorySelectVisible(true)}
|
||
>
|
||
<Text style={styles.categorySelectText}>
|
||
{TICKET_CATEGORIES.find((c) => c.value === category)?.label || 'Select category'}
|
||
</Text>
|
||
<Text style={{ color: colors.textSecondary }}>▼</Text>
|
||
</TouchableOpacity>
|
||
|
||
<Text style={styles.label}>Subject</Text>
|
||
<TextInput
|
||
style={styles.input}
|
||
value={subject}
|
||
onChangeText={setSubject}
|
||
placeholder="What is the issue about?"
|
||
placeholderTextColor={colors.textSecondary}
|
||
/>
|
||
|
||
<Text style={styles.label}>Description</Text>
|
||
<TextInput
|
||
style={[styles.input, styles.textArea]}
|
||
value={description}
|
||
onChangeText={setDescription}
|
||
placeholder="Please describe your issue in detail..."
|
||
placeholderTextColor={colors.textSecondary}
|
||
multiline
|
||
numberOfLines={4}
|
||
/>
|
||
|
||
<View style={styles.modalActions}>
|
||
<TouchableOpacity
|
||
style={[styles.modalBtn, styles.cancelBtn]}
|
||
onPress={() => setCreateModalVisible(false)}
|
||
>
|
||
<Text style={styles.cancelBtnText}>Cancel</Text>
|
||
</TouchableOpacity>
|
||
<TouchableOpacity
|
||
style={[styles.modalBtn, styles.submitBtn]}
|
||
onPress={handleCreateTicketSubmit}
|
||
>
|
||
<Text style={styles.submitBtnText}>Submit</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
</ScrollView>
|
||
</View>
|
||
</View>
|
||
</Modal>
|
||
|
||
{/* Category Dropdown Modal */}
|
||
<Modal
|
||
visible={categorySelectVisible}
|
||
transparent
|
||
animationType="fade"
|
||
onRequestClose={() => setCategorySelectVisible(false)}
|
||
>
|
||
<View style={{ flex: 1, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'center', padding: 24 }}>
|
||
<View style={{ backgroundColor: colors.cardBg, borderRadius: 12, padding: 16 }}>
|
||
<Text style={{ fontSize: 18, fontWeight: 'bold', color: colors.text, marginBottom: 12, textAlign: 'center' }}>
|
||
Select Category
|
||
</Text>
|
||
{TICKET_CATEGORIES.map((item) => (
|
||
<TouchableOpacity
|
||
key={item.value}
|
||
style={{ paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: colors.border }}
|
||
onPress={() => {
|
||
setCategory(item.value);
|
||
setCategorySelectVisible(false);
|
||
}}
|
||
>
|
||
<Text style={{ fontSize: 16, color: colors.text }}>{item.label}</Text>
|
||
</TouchableOpacity>
|
||
))}
|
||
<TouchableOpacity
|
||
style={{ marginTop: 16, paddingVertical: 12, alignItems: 'center' }}
|
||
onPress={() => setCategorySelectVisible(false)}
|
||
>
|
||
<Text style={{ color: colors.primary, fontWeight: 'bold' }}>Close</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
</View>
|
||
</Modal>
|
||
</View>
|
||
);
|
||
};
|
||
export default HelpSupportScreen;
|