import React, { useState, useEffect, useCallback } from "react"; import { View, Text, ScrollView, StyleSheet, TouchableOpacity, TextInput, Alert, ActivityIndicator, Keyboard, } from "react-native"; import { useFocusEffect } from "@react-navigation/native"; import Slider from "@react-native-community/slider"; import CylinderSlider from "../components/CylinderSlinder"; import { setConveyorSpeed, getConveyorSpeed } from "../services/api"; // ─── Konstanta ──────────────────────────────────────────────────────────────── const SPEED_MODES = [ { key: "pelan", label: "Pelan", value: 50, desc: "Cocok untuk telur rapuh" }, { key: "sedang", label: "Sedang", value: 128, desc: "Mode normal" }, { key: "cepat", label: "Cepat", value: 220, desc: "Throughput tinggi" }, ]; function getSpeedLabel(v: number) { if (v < 80) return "Pelan"; if (v < 170) return "Sedang"; return "Cepat"; } function getSpeedColor(v: number) { if (v < 80) return "#16a34a"; if (v < 170) return "#d97706"; return "#dc2626"; } // ─── Komponen utama ─────────────────────────────────────────────────────────── export default function ControlScreen() { const [speed, setSpeed] = useState(128); const [speedMode, setSpeedMode] = useState("sedang"); const [speedInput, setSpeedInput] = useState("128"); const [saving, setSaving] = useState(false); const [saved, setSaved] = useState(false); const [loadingCurrent, setLoadingCurrent] = useState(true); const [refreshing, setRefreshing] = useState(false); // ── Fetch speed dari server ─────────────────────────────────────────────── const fetchSpeed = useCallback(async (isManual = false) => { if (isManual) setRefreshing(true); else setLoadingCurrent(true); try { const currentSpeed = await getConveyorSpeed(); setSpeed(currentSpeed); setSpeedInput(String(currentSpeed)); const match = SPEED_MODES.find((m) => m.value === currentSpeed); setSpeedMode(match ? match.key : ""); } catch { if (isManual) { Alert.alert("Gagal", "Tidak dapat mengambil data dari server."); } } finally { setLoadingCurrent(false); setRefreshing(false); } }, []); // Fetch pertama kali saat app buka useEffect(() => { fetchSpeed(); }, []); // ✅ Fetch ulang SETIAP KALI tab Control difokuskan (balik dari tab lain) // Ini memastikan data selalu sinkron dengan server, bukan dari state lama useFocusEffect( useCallback(() => { fetchSpeed(); }, [fetchSpeed]) ); // ── Handler ─────────────────────────────────────────────────────────────── const applySpeedMode = (key: string, value: number) => { setSpeedMode(key); setSpeed(value); setSpeedInput(String(value)); }; // Dipanggil dari cylinder atau slider — TIDAK dari input teks const handleSpeedChange = (v: number) => { const rounded = Math.round(v); setSpeed(rounded); setSpeedInput(String(rounded)); const match = SPEED_MODES.find((m) => m.value === rounded); setSpeedMode(match ? match.key : ""); }; /** * Terapkan nilai dari input teks. * HANYA dipanggil saat tombol "Terapkan" diklik — BUKAN saat blur/dismiss keyboard. */ const handleApplyInput = () => { Keyboard.dismiss(); const val = Math.max(0, Math.min(255, parseInt(speedInput, 10) || 0)); setSpeed(val); setSpeedInput(String(val)); const match = SPEED_MODES.find((m) => m.value === val); setSpeedMode(match ? match.key : ""); }; const handleSave = async () => { try { setSaving(true); await setConveyorSpeed(speed); setSaved(true); setTimeout(() => setSaved(false), 2500); } catch { Alert.alert("Error", "Gagal mengirim data ke perangkat. Cek koneksi server."); } finally { setSaving(false); } }; const activeColor = getSpeedColor(speed); // ── Loading state ───────────────────────────────────────────────────────── if (loadingCurrent) { return ( Memuat konfigurasi... ); } // ── Render ──────────────────────────────────────────────────────────────── return ( {/* Header */} Kontrol Conveyor Atur kecepatan laju conveyor fetchSpeed(true)} disabled={refreshing} style={[styles.refreshBtn, refreshing && { opacity: 0.5 }]} > {refreshing ? : } {/* Cylinder */} {Math.round((speed / 255) * 100)}% Geser ke atas / bawah untuk adjust {/* Control Card */} {/* Header nilai */} ⚙️ Kecepatan Conveyor {speed} / 255 {getSpeedLabel(speed)} {/* Mode preset */} Mode Cepat {SPEED_MODES.map((m) => { const active = speedMode === m.key; const mColor = getSpeedColor(m.value); return ( applySpeedMode(m.key, m.value)} style={[ styles.modeButton, { borderColor: active ? mColor : "#e2e8f0", backgroundColor: active ? mColor + "15" : "#f8fafc", }, ]} > {m.label} {m.desc} {active && ( )} ); })} {/* Slider */} Slider Manual 0 255 {/* Input angka — onBlur DIHAPUS, hanya terapkan via tombol */} Input Angka (0–255) {/* Tombol Terapkan — SATU-SATUNYA cara nilai input diterapkan */} Terapkan {/* Speed bar indikator */} {speed < 80 ? "⚠️ Kecepatan rendah — pastikan conveyor bergerak" : speed > 200 ? "⚠️ Kecepatan tinggi — awasi kondisi telur" : "✓ Kecepatan normal"} {/* Tombol kirim */} {saving ? "Mengirim..." : saved ? "✓ Kecepatan Tersimpan!" : "Kirim ke Perangkat"} ); } // ─── Styles ─────────────────────────────────────────────────────────────────── const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#f8fafc" }, content: { padding: 16, paddingBottom: 32 }, loadingContainer: { flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: "#f8fafc", gap: 12 }, loadingText: { fontSize: 13, color: "#94a3b8" }, header: { flexDirection: "row", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 20 }, title: { fontSize: 24, fontWeight: "800", color: "#0f172a", letterSpacing: -0.5 }, subtitle: { fontSize: 12, color: "#94a3b8", marginTop: 2 }, refreshBtn: { width: 40, height: 40, borderRadius: 20, backgroundColor: "#fff", borderWidth: 1, borderColor: "#e2e8f0", alignItems: "center", justifyContent: "center", shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.05, shadowRadius: 3, elevation: 2, }, refreshIcon: { fontSize: 22, color: "#0284c7", fontWeight: "700", lineHeight: 26 }, // Cylinder card cylinderCard: { backgroundColor: "#fff", borderWidth: 1, borderColor: "#e2e8f0", borderRadius: 16, paddingVertical: 28, paddingHorizontal: 16, marginBottom: 16, alignItems: "center", shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.05, shadowRadius: 4, elevation: 2, }, cylinderWrapper: { alignItems: "center", gap: 16 }, cylinderInfo: { alignItems: "center" }, cylinderPct: { fontSize: 32, fontWeight: "800", color: "#0f172a", fontFamily: "monospace" }, cylinderHint: { fontSize: 11, color: "#94a3b8", marginTop: 2 }, // Control card controlCard: { backgroundColor: "#fff", borderWidth: 1, borderRadius: 16, padding: 16, marginBottom: 12, shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.04, shadowRadius: 3, elevation: 1, }, controlHeaderRow: { flexDirection: "row", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 16, }, controlLabel: { fontSize: 12, fontWeight: "700", marginBottom: 4 }, valueRow: { flexDirection: "row", alignItems: "baseline" }, controlValueText: { fontSize: 32, fontWeight: "800", fontFamily: "monospace" }, controlValueUnit: { fontSize: 13, color: "#94a3b8" }, modeBadge: { paddingHorizontal: 12, paddingVertical: 5, borderRadius: 20, borderWidth: 1 }, modeBadgeText: { fontSize: 11, fontWeight: "600" }, sectionLabel: { fontSize: 10, color: "#94a3b8", fontWeight: "600", marginBottom: 8, marginTop: 4 }, // Mode buttons modeRow: { flexDirection: "row", gap: 8, marginBottom: 16 }, modeButton: { flex: 1, paddingVertical: 10, paddingHorizontal: 6, borderRadius: 12, borderWidth: 1, alignItems: "center", gap: 3, position: "relative" }, modeButtonLabel:{ fontSize: 11, fontWeight: "700" }, modeButtonDesc: { fontSize: 9, color: "#94a3b8", textAlign: "center" }, activeIndicator:{ position: "absolute", top: 6, right: 6, width: 6, height: 6, borderRadius: 3 }, // Slider sliderRow: { flexDirection: "row", alignItems: "center", gap: 8, marginBottom: 8 }, sliderMin: { fontSize: 10, color: "#94a3b8", width: 16, textAlign: "center" }, sliderMax: { fontSize: 10, color: "#94a3b8", width: 24, textAlign: "center" }, slider: { flex: 1, height: 40 }, // Input angka inputRow: { flexDirection: "row", gap: 10, alignItems: "center" }, numberInput: { width: 80, backgroundColor: "#f8fafc", borderWidth: 1.5, borderRadius: 10, paddingHorizontal: 10, paddingVertical: 9, color: "#0f172a", fontSize: 18, fontFamily: "monospace", textAlign: "center", fontWeight: "700", }, applyButton: { flex: 1, paddingVertical: 12, borderRadius: 10, borderWidth: 1, alignItems: "center" }, applyButtonText: { fontSize: 13, fontWeight: "700" }, inputHint: { fontSize: 10, color: "#cbd5e1", marginTop: 6, fontStyle: "italic" }, // Speed bar speedBar: { backgroundColor: "#f1f5f9", borderRadius: 12, overflow: "hidden", marginBottom: 16, height: 40, justifyContent: "center", borderWidth: 1, borderColor: "#e2e8f0", }, speedBarFill: { position: "absolute", top: 0, left: 0, bottom: 0, opacity: 0.15, borderRadius: 12 }, speedBarLabel: { fontSize: 11, color: "#64748b", paddingHorizontal: 12 }, // Save button saveButton: { borderRadius: 14, paddingVertical: 16, alignItems: "center" }, saveButtonText: { color: "#fff", fontSize: 15, fontWeight: "800", letterSpacing: 0.3 }, });