384 lines
9.5 KiB
TypeScript
384 lines
9.5 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import {
|
|
View,
|
|
Text,
|
|
StyleSheet,
|
|
ScrollView,
|
|
Image,
|
|
Pressable,
|
|
Alert,
|
|
} from "react-native";
|
|
import { SafeAreaView } from "react-native-safe-area-context";
|
|
import { Ionicons } from "@expo/vector-icons";
|
|
import { Redirect, useRouter } from "expo-router";
|
|
|
|
import { COLORS } from "../constants/colors";
|
|
import { useApp } from "../context/AppContext";
|
|
import { getMyAddress, type MyAddress } from "../services/addresses.service";
|
|
import { apiFetch } from "../lib/api";
|
|
|
|
type Row = {
|
|
key: string;
|
|
title: string;
|
|
subtitle: string;
|
|
icon: keyof typeof Ionicons.glyphMap;
|
|
color: string;
|
|
onPress: () => void;
|
|
danger?: boolean;
|
|
};
|
|
|
|
export default function ProfileScreen() {
|
|
const { user, logout } = useApp();
|
|
const router = useRouter();
|
|
const [myAddress, setMyAddress] = useState<MyAddress | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!user) return;
|
|
void getMyAddress().then(setMyAddress).catch(() => {});
|
|
}, [user]);
|
|
|
|
if (!user) return <Redirect href="/login" />;
|
|
|
|
const handleLogout = () => {
|
|
logout();
|
|
router.replace("/login");
|
|
};
|
|
|
|
const handleReset = async () => {
|
|
try {
|
|
await apiFetch<{ message: string }>("/api/tracking/reset-demo", {
|
|
method: "POST",
|
|
body: JSON.stringify({}),
|
|
});
|
|
Alert.alert("Demo reiniciada", "Las notificaciones se borraron.");
|
|
} catch (err) {
|
|
Alert.alert(
|
|
"No se pudo reiniciar",
|
|
err instanceof Error ? err.message : "Error",
|
|
);
|
|
}
|
|
};
|
|
|
|
const handleHelp = () => {
|
|
Alert.alert(
|
|
"Ayuda",
|
|
"OptiRuta te avisa cuando el camión recolector está cerca.\n\n" +
|
|
"• Tu dirección define la ruta que verás.\n" +
|
|
"• Las alertas se actualizan cada 30 segundos.\n" +
|
|
"• Usa el buzón si el camión no pasó.",
|
|
);
|
|
};
|
|
|
|
const rows: Row[] = [
|
|
{
|
|
key: "address",
|
|
title: "Mi domicilio",
|
|
subtitle: "Ver y administrar tus domicilios registrados",
|
|
icon: "home-outline",
|
|
color: "#0E8A61",
|
|
onPress: () => router.push("/addresses"),
|
|
},
|
|
{
|
|
key: "feedback",
|
|
title: "Buzón de retroalimentación",
|
|
subtitle: "Sugerencias, comentarios y reportes",
|
|
icon: "chatbubble-outline",
|
|
color: "#0E8A61",
|
|
onPress: () => router.push("/feedback"),
|
|
},
|
|
{
|
|
key: "reset",
|
|
title: "Reiniciar demo",
|
|
subtitle: "Restablecer la simulación y los datos",
|
|
icon: "refresh-outline",
|
|
color: "#0E8A61",
|
|
onPress: handleReset,
|
|
},
|
|
{
|
|
key: "help",
|
|
title: "Ayuda",
|
|
subtitle: "Preguntas frecuentes y soporte",
|
|
icon: "help-circle-outline",
|
|
color: "#0E8A61",
|
|
onPress: handleHelp,
|
|
},
|
|
{
|
|
key: "logout",
|
|
title: "Cerrar sesión",
|
|
subtitle: "Salir de tu cuenta",
|
|
icon: "log-out-outline",
|
|
color: "#EF4444",
|
|
onPress: handleLogout,
|
|
danger: true,
|
|
},
|
|
];
|
|
|
|
const initial = (user.name?.[0] ?? user.email[0]).toUpperCase();
|
|
|
|
return (
|
|
<SafeAreaView
|
|
style={{ flex: 1, backgroundColor: COLORS.background }}
|
|
edges={["bottom"]}
|
|
>
|
|
<ScrollView
|
|
showsVerticalScrollIndicator={false}
|
|
contentContainerStyle={{ paddingBottom: 130 }}
|
|
>
|
|
{/* Header verde con avatar */}
|
|
<View style={styles.heroWrap}>
|
|
<View style={styles.heroBackground} />
|
|
<View style={styles.avatarOuter}>
|
|
<View style={styles.avatarInner}>
|
|
<Text style={styles.avatarText}>{initial}</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
<View style={styles.identityBox}>
|
|
<Text style={styles.name}>
|
|
Hola, {user.name} <Text style={{ color: "#22C55E" }}>🌿</Text>
|
|
</Text>
|
|
<View style={styles.emailRow}>
|
|
<Ionicons name="mail-outline" size={14} color="#6B7280" />
|
|
<Text style={styles.email}>{user.email}</Text>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Zona + ruta */}
|
|
<Pressable
|
|
style={styles.zoneRow}
|
|
onPress={() => router.push("/addresses")}
|
|
>
|
|
<View style={styles.zoneCol}>
|
|
<Ionicons name="location-outline" size={22} color="#0E8A61" />
|
|
<View style={{ marginLeft: 8, flex: 1 }}>
|
|
<Text style={styles.zoneLabel}>Tu zona</Text>
|
|
<Text style={styles.zoneValue} numberOfLines={2}>
|
|
{myAddress?.colonia ?? "Sin asignar"}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
<View style={styles.zoneDivider} />
|
|
<View style={styles.zoneCol}>
|
|
<Ionicons name="bus-outline" size={22} color="#0E8A61" />
|
|
<View style={{ marginLeft: 8, flex: 1 }}>
|
|
<Text style={styles.zoneLabel}>Ruta asignada</Text>
|
|
<Text style={styles.zoneValue} numberOfLines={1}>
|
|
{myAddress?.routeId ?? "—"}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
<Ionicons
|
|
name="chevron-forward"
|
|
size={18}
|
|
color="#9CA3AF"
|
|
style={{ marginLeft: 4 }}
|
|
/>
|
|
</Pressable>
|
|
|
|
{/* Lista de opciones */}
|
|
<View style={{ paddingHorizontal: 16, marginTop: 4 }}>
|
|
{rows.map((r) => (
|
|
<Pressable key={r.key} style={styles.rowCard} onPress={r.onPress}>
|
|
<View
|
|
style={[
|
|
styles.rowIcon,
|
|
{ backgroundColor: `${r.color}15` },
|
|
]}
|
|
>
|
|
<Ionicons name={r.icon} size={22} color={r.color} />
|
|
</View>
|
|
<View style={{ flex: 1 }}>
|
|
<Text
|
|
style={[
|
|
styles.rowTitle,
|
|
r.danger && { color: "#EF4444" },
|
|
]}
|
|
>
|
|
{r.title}
|
|
</Text>
|
|
<Text style={styles.rowSubtitle}>{r.subtitle}</Text>
|
|
</View>
|
|
<Ionicons name="chevron-forward" size={18} color="#9CA3AF" />
|
|
</Pressable>
|
|
))}
|
|
</View>
|
|
|
|
{/* Tu impacto cuenta */}
|
|
<View style={styles.impactCard}>
|
|
<View style={styles.impactLeaf}>
|
|
<Image
|
|
source={require("../../assets/illustrations/impact-leaf.png")}
|
|
style={{ width: 40, height: 40 }}
|
|
resizeMode="contain"
|
|
/>
|
|
</View>
|
|
<View style={{ flex: 1 }}>
|
|
<Text style={styles.impactTitle}>Tu impacto cuenta</Text>
|
|
<Text style={styles.impactBody}>
|
|
Siguiendo los horarios y separando correctamente, haces tu ciudad
|
|
más limpia.
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</ScrollView>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
heroWrap: {
|
|
height: 140,
|
|
backgroundColor: "#D1FAE5",
|
|
overflow: "visible",
|
|
marginBottom: 60,
|
|
},
|
|
heroBackground: {
|
|
position: "absolute",
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
backgroundColor: "#D1FAE5",
|
|
},
|
|
avatarOuter: {
|
|
position: "absolute",
|
|
bottom: -50,
|
|
alignSelf: "center",
|
|
width: 112,
|
|
height: 112,
|
|
borderRadius: 56,
|
|
backgroundColor: "#FFFFFF",
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
shadowColor: "#000",
|
|
shadowOffset: { width: 0, height: 4 },
|
|
shadowOpacity: 0.1,
|
|
shadowRadius: 8,
|
|
elevation: 6,
|
|
},
|
|
avatarInner: {
|
|
width: 100,
|
|
height: 100,
|
|
borderRadius: 50,
|
|
backgroundColor: "#0E8A61",
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
},
|
|
avatarText: {
|
|
color: "#FFFFFF",
|
|
fontSize: 38,
|
|
fontWeight: "800",
|
|
},
|
|
identityBox: {
|
|
alignItems: "center",
|
|
marginBottom: 18,
|
|
},
|
|
name: {
|
|
fontSize: 22,
|
|
fontWeight: "800",
|
|
color: "#0F172A",
|
|
},
|
|
emailRow: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
marginTop: 6,
|
|
},
|
|
email: {
|
|
fontSize: 13,
|
|
color: "#6B7280",
|
|
marginLeft: 6,
|
|
},
|
|
zoneRow: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
backgroundColor: "#ECFDF5",
|
|
borderRadius: 16,
|
|
padding: 14,
|
|
marginHorizontal: 16,
|
|
marginBottom: 16,
|
|
},
|
|
zoneCol: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
flex: 1,
|
|
},
|
|
zoneDivider: {
|
|
width: 1,
|
|
height: 36,
|
|
backgroundColor: "#A7F3D0",
|
|
marginHorizontal: 8,
|
|
},
|
|
zoneLabel: {
|
|
fontSize: 11,
|
|
color: "#065F46",
|
|
fontWeight: "700",
|
|
},
|
|
zoneValue: {
|
|
fontSize: 13,
|
|
color: "#0F172A",
|
|
fontWeight: "600",
|
|
marginTop: 2,
|
|
},
|
|
rowCard: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
backgroundColor: "#FFFFFF",
|
|
borderRadius: 14,
|
|
padding: 14,
|
|
marginBottom: 10,
|
|
shadowColor: "#000",
|
|
shadowOffset: { width: 0, height: 2 },
|
|
shadowOpacity: 0.05,
|
|
shadowRadius: 6,
|
|
elevation: 2,
|
|
},
|
|
rowIcon: {
|
|
width: 42,
|
|
height: 42,
|
|
borderRadius: 21,
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
marginRight: 12,
|
|
},
|
|
rowTitle: {
|
|
fontSize: 15,
|
|
fontWeight: "700",
|
|
color: "#0F172A",
|
|
},
|
|
rowSubtitle: {
|
|
fontSize: 12,
|
|
color: "#6B7280",
|
|
marginTop: 2,
|
|
},
|
|
impactCard: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
backgroundColor: "#ECFDF5",
|
|
borderRadius: 16,
|
|
padding: 14,
|
|
marginHorizontal: 16,
|
|
marginTop: 8,
|
|
},
|
|
impactLeaf: {
|
|
width: 50,
|
|
height: 50,
|
|
borderRadius: 25,
|
|
backgroundColor: "#FFFFFF",
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
marginRight: 12,
|
|
},
|
|
impactTitle: {
|
|
fontSize: 14,
|
|
fontWeight: "800",
|
|
color: "#065F46",
|
|
marginBottom: 4,
|
|
},
|
|
impactBody: {
|
|
fontSize: 12,
|
|
color: "#065F46",
|
|
lineHeight: 16,
|
|
},
|
|
});
|