This commit is contained in:
2026-02-14 00:36:50 +00:00
commit d974371976

265
index.html Normal file
View File

@@ -0,0 +1,265 @@
import React, { useState, useEffect } from 'react';
import { User, MessageSquare, Palette, Smartphone, X, ChevronRight, ExternalLink } from 'lucide-react';
// --- COMPONENTE PRINCIPAL ---
export default function App() {
const [userData, setUserData] = useState(null);
const [platformData, setPlatformData] = useState(null);
const [themeData, setThemeData] = useState(null);
const [chatData, setChatData] = useState(null);
// Estado para el Modal
const [selectedCategory, setSelectedCategory] = useState(null);
useEffect(() => {
const tg = window.Telegram?.WebApp;
if (tg && tg.initDataUnsafe && Object.keys(tg.initDataUnsafe).length > 0) {
// --- ENTORNO REAL DE TELEGRAM ---
tg.ready();
tg.expand();
// Configurar colores de la barra de estado de Telegram
if (tg.setHeaderColor) tg.setHeaderColor('#3d0091');
if (tg.setBackgroundColor) tg.setBackgroundColor('#121212');
setUserData(tg.initDataUnsafe.user);
setChatData({
start_param: tg.initDataUnsafe.start_param || 'N/A',
chat_type: tg.initDataUnsafe.chat_type || 'private',
chat_instance: tg.initDataUnsafe.chat_instance || 'N/A',
});
setThemeData(tg.themeParams);
setPlatformData({
platform: tg.platform,
version: tg.version,
viewportHeight: tg.viewportHeight,
colorScheme: tg.colorScheme
});
} else {
// --- MODO DESARROLLO (MOCK DATA) ---
console.log("Modo desarrollo: Cargando datos Mock");
setUserData({
id: 987654321,
first_name: "Dev",
last_name: "Tester",
username: "dev_tester",
language_code: "en",
is_premium: false,
photo_url: null
});
setChatData({
start_param: "debug_mode_on",
chat_type: "supergroup",
chat_instance: "1122334455"
});
setChatData({
start_param: "referral_source_ads",
chat_type: "private",
chat_instance: "84758473829102"
});
setThemeData({
bg_color: "#121212",
text_color: "#ffffff",
hint_color: "#999999",
link_color: "#7c4dff",
button_color: "#3d0091",
button_text_color: "#ffffff"
});
setPlatformData({
platform: "ios",
version: "7.0",
viewportHeight: 640,
colorScheme: "dark"
});
}
}, []);
// Función para cerrar el modal
const closeModal = () => setSelectedCategory(null);
// Renderizado de lista de detalles
const renderDetails = (data) => {
if (!data) return <p className="text-gray-400">Sin datos disponibles.</p>;
return Object.entries(data).map(([key, value]) => (
<div key={key} className="flex flex-col py-3 border-b border-white/10 last:border-0">
<span className="text-xs font-semibold text-purple-300 uppercase tracking-wider mb-1">
{key.replace(/_/g, ' ')}
</span>
<span className="text-white text-sm break-all font-mono bg-black/20 p-2 rounded">
{value !== null && value !== undefined ? value.toString() : 'N/A'}
</span>
</div>
));
};
return (
<div className="min-h-screen bg-[#121212] text-white font-sans selection:bg-purple-500/30">
{/* --- HEADER CURVO --- */}
<div className="relative bg-[#3d0091] pt-8 pb-12 rounded-b-[40px] shadow-2xl shadow-purple-900/20">
<div className="flex flex-col items-center justify-center px-4">
{/* Avatar con anillo animado */}
<div className="relative group cursor-pointer">
<div className="absolute -inset-1 bg-gradient-to-r from-pink-500 to-purple-600 rounded-full opacity-75 group-hover:opacity-100 blur transition duration-200"></div>
<div className="relative w-24 h-24 rounded-full bg-[#1e1e1e] border-4 border-[#3d0091] flex items-center justify-center overflow-hidden">
{userData?.photo_url ? (
<img src={userData.photo_url} alt="User" className="w-full h-full object-cover" />
) : (
<span className="text-3xl font-bold text-white">
{userData?.first_name ? userData.first_name[0] : 'U'}
</span>
)}
</div>
</div>
<h1 className="mt-4 text-2xl font-bold tracking-tight text-white">
Hola, {userData?.first_name || 'Usuario'}
</h1>
{userData?.username && (
<div className="mt-2 px-3 py-1 bg-white/10 backdrop-blur-md rounded-full border border-white/10 text-sm font-medium text-purple-200">
@{userData.username}
</div>
)}
</div>
</div>
{/* --- CONTENIDO PRINCIPAL --- */}
<div className="px-6 -mt-6 pb-10 relative z-10">
<div className="text-center mb-4 opacity-75 text-xs font-medium uppercase tracking-widest text-purple-200">
Panel de Control
</div>
{/* GRID 2x2 - USO DE TAILWIND GRID QUE ES MUCHO MÁS ROBUSTO */}
<div className="grid grid-cols-2 gap-4 w-full max-w-md mx-auto">
{/* TARJETA 1: USUARIO */}
<CardButton
icon={<User size={32} className="text-purple-400" />}
label="USUARIO"
color="bg-purple-500/10"
borderColor="border-purple-500/20"
onClick={() => setSelectedCategory({ title: 'Datos de Usuario', data: userData, icon: <User /> })}
/>
{/* TARJETA 2: CHAT */}
<CardButton
icon={<MessageSquare size={32} className="text-pink-400" />}
label="CHAT"
color="bg-pink-500/10"
borderColor="border-pink-500/20"
onClick={() => setSelectedCategory({ title: 'Contexto Chat', data: chatData, icon: <MessageSquare /> })}
/>
{/* TARJETA 3: TEMA */}
<CardButton
icon={<Palette size={32} className="text-emerald-400" />}
label="TEMA"
color="bg-emerald-500/10"
borderColor="border-emerald-500/20"
onClick={() => setSelectedCategory({ title: 'Apariencia', data: themeData, icon: <Palette /> })}
/>
{/* TARJETA 4: SISTEMA */}
<CardButton
icon={<Smartphone size={32} className="text-orange-400" />}
label="SISTEMA"
color="bg-orange-500/10"
borderColor="border-orange-500/20"
onClick={() => setSelectedCategory({ title: 'Dispositivo', data: platformData, icon: <Smartphone /> })}
/>
</div>
{/* Botón de documentación */}
<div className="mt-8 text-center">
<button
onClick={() => window.open('https://core.telegram.org/bots/webapps', '_blank')}
className="inline-flex items-center gap-2 text-sm text-gray-500 hover:text-purple-400 transition-colors"
>
<span>Documentación API</span>
<ExternalLink size={14} />
</button>
</div>
</div>
{/* --- MODAL PERSONALIZADO (Sin librerías pesadas) --- */}
{selectedCategory && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 animate-in fade-in duration-200">
{/* Overlay oscuro */}
<div
className="absolute inset-0 bg-black/80 backdrop-blur-sm"
onClick={closeModal}
></div>
{/* Contenido del Modal */}
<div className="relative w-full max-w-sm bg-[#1e1e1e] rounded-2xl shadow-2xl border border-white/10 overflow-hidden transform transition-all scale-100">
{/* Cabecera del Modal */}
<div className="flex items-center justify-between p-4 border-b border-white/10 bg-[#252525]">
<div className="flex items-center gap-3 text-white">
<div className="p-2 bg-white/5 rounded-lg text-purple-400">
{selectedCategory.icon}
</div>
<h3 className="font-bold text-lg">{selectedCategory.title}</h3>
</div>
<button
onClick={closeModal}
className="p-2 text-gray-400 hover:text-white hover:bg-white/10 rounded-full transition-colors"
>
<X size={20} />
</button>
</div>
{/* Cuerpo del Modal */}
<div className="p-4 max-h-[60vh] overflow-y-auto">
{renderDetails(selectedCategory.data)}
</div>
{/* Pie del Modal */}
<div className="p-4 bg-[#252525] border-t border-white/10">
<button
onClick={closeModal}
className="w-full py-3 bg-[#3d0091] hover:bg-[#4d00b1] text-white font-bold rounded-xl transition-all active:scale-[0.98]"
>
Cerrar
</button>
</div>
</div>
</div>
)}
</div>
);
}
// --- COMPONENTE DE TARJETA REUTILIZABLE ---
// Este componente garantiza la forma cuadrada con 'aspect-square'
function CardButton({ icon, label, color, borderColor, onClick }) {
return (
<button
onClick={onClick}
className={`
relative group flex flex-col items-center justify-center
aspect-square w-full rounded-2xl
${color} border ${borderColor}
hover:brightness-125 active:scale-95
transition-all duration-200 ease-out
`}
>
<div className="mb-3 transform group-hover:scale-110 transition-transform duration-200">
{icon}
</div>
<span className="text-xs font-bold tracking-wider text-white/90">
{label}
</span>
{/* Indicador sutil de flecha */}
<div className="absolute top-3 right-3 opacity-0 group-hover:opacity-50 transition-opacity">
<ChevronRight size={16} className="text-white" />
</div>
</button>
);
}