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([ { 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(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 ( item.id} contentContainerStyle={styles.chatList} onContentSizeChange={() => flatListRef.current?.scrollToEnd({animated: true})} renderItem={({item}) => ( {item.from === 'bot' && ( 🤖 )} {item.text} {item.time} )} /> {isTyping && ( Bot sedang menganalisis... )} item.id} renderItem={({item}) => ( handleChat(item.label)}> {item.label} )} /> handleChat(inputText)} /> handleChat(inputText)}> Kirim ); }; 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;