TKK_E32231332/src/Containers/notifikasi/components/BotTab.tsx

207 lines
7.2 KiB
TypeScript

import React, {useState, useRef} from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
FlatList,
KeyboardAvoidingView,
Platform,
ActivityIndicator,
TextInput,
Alert,
} from 'react-native';
interface ChatMessage {
id: string;
from: 'user' | 'bot';
text: string;
time: string;
}
const QUICK_REPLIES = [
{id: 'status', label: '📊 Kondisi Hari Ini'},
{id: 'perawatan', label: '🌿 Saran Perawatan'},
{id: 'nutrisi', label: '🌾 Cek Nutrisi'},
{id: 'suhu', label: '🌡️ Cek Suhu'},
];
/**
* PENTING: Gunakan HTTPS untuk Ngrok.
* Tambahkan 'ngrok-skip-browser-warning': 'true' di headers agar APK bisa tembus.
*/
const BOT_API_URL = 'http://202.10.40.129:3000/api/bot/chat';
const BotTab: React.FC = () => {
const [messages, setMessages] = useState<ChatMessage[]>([
{
id: '1',
from: 'bot',
text: 'Halo! Saya Bot Greenhouse PT Agrofilia Permata 🌱\n\nAda yang bisa saya bantu terkait budidaya vanili Anda hari ini?',
time: new Date().toLocaleTimeString('id-ID', {hour: '2-digit', minute: '2-digit'}),
},
]);
const [inputText, setInputText] = useState('');
const [isTyping, setIsTyping] = useState(false);
const flatListRef = useRef<FlatList>(null);
const handleChat = async (input: string) => {
if (!input.trim()) return;
const now = new Date().toLocaleTimeString('id-ID', {hour: '2-digit', minute: '2-digit'});
const userMsg: ChatMessage = {
id: Date.now().toString(),
from: 'user',
text: input,
time: now,
};
setMessages(prev => [...prev, userMsg]);
setInputText('');
setIsTyping(true);
try {
// Menambahkan timeout agar aplikasi tidak hang jika sinyal buruk
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 15000);
const response = await fetch(BOT_API_URL, {
method: 'POST',
signal: controller.signal,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
// Header Wajib untuk Ngrok agar tidak kena blokir halaman peringatan
'ngrok-skip-browser-warning': 'true',
},
body: JSON.stringify({message: input}),
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`Server Error: ${response.status}`);
}
const result = await response.json();
const botMsg: ChatMessage = {
id: (Date.now() + 1).toString(),
from: 'bot',
text: result.reply || 'Maaf, saya tidak mendapatkan jawaban.',
time: now,
};
setMessages(prev => [...prev, botMsg]);
} catch (err: any) {
console.error('Connection Error:', err);
let errorText = '⚠️ Koneksi gagal. Pastikan server laptop aktif dan Ngrok berjalan.';
if (err.name === 'AbortError') {
errorText = '⚠️ Request Timeout. Koneksi internet terlalu lambat.';
}
const errMsg: ChatMessage = {
id: (Date.now() + 1).toString(),
from: 'bot',
text: errorText,
time: now,
};
setMessages(prev => [...prev, errMsg]);
} finally {
setIsTyping(false);
setTimeout(() => flatListRef.current?.scrollToEnd({animated: true}), 100);
}
};
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
keyboardVerticalOffset={90}>
<FlatList
ref={flatListRef}
data={messages}
keyExtractor={item => item.id}
contentContainerStyle={styles.chatList}
onContentSizeChange={() => flatListRef.current?.scrollToEnd({animated: true})}
renderItem={({item}) => (
<View style={[styles.bubbleWrapper, item.from === 'user' ? styles.bubbleRight : styles.bubbleLeft]}>
{item.from === 'bot' && (
<View style={styles.botAvatar}>
<Text>🤖</Text>
</View>
)}
<View style={[styles.bubble, item.from === 'user' ? styles.bubbleUser : styles.bubbleBot]}>
<Text style={styles.chatText}>{item.text}</Text>
<Text style={styles.chatTime}>{item.time}</Text>
</View>
</View>
)}
/>
{isTyping && (
<View style={styles.typingIndicator}>
<ActivityIndicator size="small" color="#4ADE80" />
<Text style={styles.typingText}>Bot sedang menganalisis...</Text>
</View>
)}
<View style={styles.quickReplyContainer}>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={QUICK_REPLIES}
keyExtractor={item => item.id}
renderItem={({item}) => (
<TouchableOpacity
style={styles.quickReplyBtn}
onPress={() => handleChat(item.label)}>
<Text style={styles.quickReplyText}>{item.label}</Text>
</TouchableOpacity>
)}
/>
</View>
<View style={styles.inputSection}>
<TextInput
style={styles.input}
placeholder="Tanya kondisi vanili..."
placeholderTextColor="#6B9E7A"
value={inputText}
onChangeText={setInputText}
onSubmitEditing={() => handleChat(inputText)}
/>
<TouchableOpacity style={styles.sendBtn} onPress={() => handleChat(inputText)}>
<Text style={styles.sendBtnText}>Kirim</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
};
const styles = StyleSheet.create({
container: {flex: 1, backgroundColor: '#0D1A12', paddingBottom: 80},
chatList: {padding: 16, paddingBottom: 20},
bubbleWrapper: {flexDirection: 'row', marginBottom: 15, alignItems: 'flex-end'},
bubbleLeft: {justifyContent: 'flex-start'},
bubbleRight: {justifyContent: 'flex-end'},
botAvatar: {width: 30, height: 30, borderRadius: 15, backgroundColor: '#1A3A28', alignItems: 'center', justifyContent: 'center', marginRight: 8},
bubble: {maxWidth: '80%', borderRadius: 15, padding: 12},
bubbleBot: {backgroundColor: '#1A3A28', borderBottomLeftRadius: 2},
bubbleUser: {backgroundColor: '#22C55E', borderBottomRightRadius: 2},
chatText: {color: '#FFFFFF', fontSize: 14, lineHeight: 20},
chatTime: {color: '#6B9E7A', fontSize: 10, marginTop: 4, textAlign: 'right'},
typingIndicator: {flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, marginBottom: 10, gap: 8},
typingText: {color: '#4ADE80', fontSize: 12, fontStyle: 'italic'},
quickReplyContainer: {paddingVertical: 10, borderTopWidth: 0.5, borderTopColor: '#2D5A3D'},
quickReplyBtn: {backgroundColor: '#14532D', paddingHorizontal: 15, paddingVertical: 8, borderRadius: 20, marginLeft: 12, borderWidth: 1, borderColor: '#2D5A3D'},
quickReplyText: {color: '#4ADE80', fontSize: 12, fontWeight: '600'},
inputSection: {flexDirection: 'row', padding: 12, backgroundColor: '#1A3A28', alignItems: 'center'},
input: {flex: 1, backgroundColor: '#0D1A12', borderRadius: 25, paddingHorizontal: 15, color: '#FFF', height: 45},
sendBtn: {marginLeft: 10, backgroundColor: '#22C55E', paddingVertical: 10, paddingHorizontal: 20, borderRadius: 25},
sendBtnText: {color: '#FFF', fontWeight: 'bold'},
});
export default BotTab;