294 lines
13 KiB
HTML
294 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
<title>Telegram Mini App - Perfil Extendido</title>
|
|
|
|
<!-- Script Oficial de Telegram WebApp -->
|
|
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
|
|
|
<!-- React y ReactDOM -->
|
|
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
|
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
|
|
|
<!-- Babel para JSX -->
|
|
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
|
|
|
<!-- Tailwind CSS -->
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
|
|
<!-- Iconos Lucide -->
|
|
<script src="https://unpkg.com/lucide@latest"></script>
|
|
|
|
<style>
|
|
body {
|
|
background-color: var(--tg-theme-bg-color, #0f172a);
|
|
color: var(--tg-theme-text-color, #ffffff);
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
/* Custom Scrollbar */
|
|
::-webkit-scrollbar {
|
|
width: 4px;
|
|
}
|
|
::-webkit-scrollbar-track {
|
|
background: rgba(255, 255, 255, 0.05);
|
|
}
|
|
::-webkit-scrollbar-thumb {
|
|
background: rgba(255, 255, 255, 0.2);
|
|
border-radius: 10px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
|
|
<script type="text/babel">
|
|
const { useState, useEffect } = React;
|
|
|
|
// --- Componentes UI ---
|
|
|
|
const SectionTitle = ({ title }) => (
|
|
<h3 className="text-slate-400 text-xs uppercase font-bold tracking-wider mb-3 mt-6 ml-1 flex items-center gap-2">
|
|
{title}
|
|
</h3>
|
|
);
|
|
|
|
const InfoCard = ({ icon, label, value, subValue, highlight }) => {
|
|
return (
|
|
<div className="bg-slate-800/50 p-3.5 rounded-xl flex items-center justify-between mb-2 backdrop-blur-sm border border-slate-700/50 hover:bg-slate-800 transition-colors">
|
|
<div className="flex items-center gap-3 overflow-hidden">
|
|
<div className={`p-2 rounded-lg ${highlight ? 'bg-blue-500/10 text-blue-400' : 'bg-slate-700/50 text-slate-400'}`}>
|
|
{icon}
|
|
</div>
|
|
<div className="flex flex-col overflow-hidden">
|
|
<span className="text-slate-300 text-sm font-medium truncate">{label}</span>
|
|
{subValue && <span className="text-slate-500 text-xs truncate">{subValue}</span>}
|
|
</div>
|
|
</div>
|
|
<div className="text-white font-semibold text-sm text-right pl-2">
|
|
{value}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const Badge = ({ children, color = "blue" }) => {
|
|
const colors = {
|
|
blue: "bg-blue-500/20 text-blue-300 border-blue-500/30",
|
|
green: "bg-emerald-500/20 text-emerald-300 border-emerald-500/30",
|
|
yellow: "bg-amber-500/20 text-amber-300 border-amber-500/30",
|
|
purple: "bg-purple-500/20 text-purple-300 border-purple-500/30",
|
|
};
|
|
return (
|
|
<span className={`px-2 py-0.5 rounded text-xs font-medium border ${colors[color] || colors.blue}`}>
|
|
{children}
|
|
</span>
|
|
);
|
|
};
|
|
|
|
// --- App Principal ---
|
|
|
|
const App = () => {
|
|
const [data, setData] = useState(null);
|
|
const [isMock, setIsMock] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const tg = window.Telegram.WebApp;
|
|
tg.ready();
|
|
tg.expand();
|
|
|
|
// Intentar obtener datos reales
|
|
const initData = tg.initDataUnsafe;
|
|
|
|
if (initData?.user) {
|
|
// MODO TELEGRAM REAL
|
|
tg.setHeaderColor('#0f172a');
|
|
setData({
|
|
user: initData.user,
|
|
device: {
|
|
platform: tg.platform, // 'ios', 'android', 'tdesktop', 'weba'
|
|
version: tg.version,
|
|
colorScheme: tg.colorScheme, // 'light' or 'dark'
|
|
expanded: tg.isExpanded
|
|
},
|
|
session: {
|
|
auth_date: initData.auth_date,
|
|
start_param: initData.start_param || "Ninguno",
|
|
hash: initData.hash ? "Seguro (Verificado)" : "No verificado"
|
|
}
|
|
});
|
|
setIsMock(false);
|
|
} else {
|
|
// MODO VISTA PREVIA (NAVEGADOR)
|
|
setIsMock(true);
|
|
setData({
|
|
user: {
|
|
id: 987654321,
|
|
first_name: "Usuario",
|
|
last_name: "Demo",
|
|
username: "usuario_demo",
|
|
language_code: "es",
|
|
is_premium: true,
|
|
allows_write_to_pm: true,
|
|
photo_url: null
|
|
},
|
|
device: {
|
|
platform: "tdesktop", // Simulando PC
|
|
version: "7.0",
|
|
colorScheme: "dark",
|
|
expanded: true
|
|
},
|
|
session: {
|
|
auth_date: Math.floor(Date.now() / 1000),
|
|
start_param: "ref_12345",
|
|
hash: "mock_hash_123"
|
|
}
|
|
});
|
|
}
|
|
|
|
// Renderizar iconos
|
|
setTimeout(() => {
|
|
if (window.lucide) window.lucide.createIcons();
|
|
}, 100);
|
|
}, []);
|
|
|
|
if (!data) return <div className="flex h-screen items-center justify-center text-slate-500 animate-pulse">Cargando datos...</div>;
|
|
|
|
// Helpers para formatear
|
|
const formatPlatform = (p) => {
|
|
const map = {
|
|
'ios': 'iPhone / iPad',
|
|
'android': 'Android',
|
|
'tdesktop': 'PC / Mac (Escritorio)',
|
|
'weba': 'Web Browser',
|
|
'macos': 'macOS'
|
|
};
|
|
return map[p] || p;
|
|
};
|
|
|
|
const formatDate = (timestamp) => {
|
|
if (!timestamp) return "N/A";
|
|
return new Date(timestamp * 1000).toLocaleDateString() + ' ' + new Date(timestamp * 1000).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-slate-950 p-4 pb-12 font-sans select-none">
|
|
|
|
{/* Header: Foto y Nombre */}
|
|
<div className="flex flex-col items-center pt-6 pb-2">
|
|
<div className="relative group cursor-pointer transition-transform active:scale-95">
|
|
<div className="w-24 h-24 rounded-full bg-gradient-to-br from-indigo-500 to-purple-600 p-[3px] shadow-2xl shadow-indigo-500/20">
|
|
<div className="w-full h-full rounded-full bg-slate-900 flex items-center justify-center overflow-hidden">
|
|
{data.user.photo_url ? (
|
|
<img src={data.user.photo_url} className="w-full h-full object-cover" />
|
|
) : (
|
|
<span className="text-3xl font-bold text-white">{data.user.first_name[0]}</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
{data.user.is_premium && (
|
|
<div className="absolute -bottom-1 -right-1 bg-gradient-to-r from-amber-400 to-orange-500 text-white p-1.5 rounded-full border-4 border-slate-950 shadow-sm" title="Usuario Premium">
|
|
<i data-lucide="star" className="w-3.5 h-3.5 fill-white"></i>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="mt-4 text-center">
|
|
<h1 className="text-xl font-bold text-white flex items-center justify-center gap-2">
|
|
{data.user.first_name} {data.user.last_name}
|
|
</h1>
|
|
{data.user.username && (
|
|
<p className="text-indigo-400 font-medium text-sm">@{data.user.username}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Badges Rápidos */}
|
|
<div className="flex gap-2 mt-3">
|
|
{data.user.language_code && (
|
|
<Badge color="blue">{data.user.language_code.toUpperCase()}</Badge>
|
|
)}
|
|
<Badge color={data.user.allows_write_to_pm ? "green" : "yellow"}>
|
|
{data.user.allows_write_to_pm ? "Chat Activo" : "Chat Bloqueado"}
|
|
</Badge>
|
|
</div>
|
|
</div>
|
|
|
|
{isMock && (
|
|
<div className="mt-4 bg-orange-500/10 border border-orange-500/20 rounded-lg p-3 flex gap-3 items-start">
|
|
<i data-lucide="alert-triangle" className="w-5 h-5 text-orange-400 shrink-0 mt-0.5"></i>
|
|
<div className="text-xs text-orange-200/80">
|
|
<strong className="text-orange-400 block mb-1">Modo Simulación</strong>
|
|
Estás viendo datos de ejemplo porque no estás dentro de Telegram.
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* SECCIÓN 1: Identificación */}
|
|
<SectionTitle title="Identificación" />
|
|
<InfoCard
|
|
icon={<i data-lucide="fingerprint" className="w-5 h-5"></i>}
|
|
label="User ID"
|
|
value={<span className="font-mono text-slate-200">{data.user.id}</span>}
|
|
highlight
|
|
/>
|
|
<InfoCard
|
|
icon={<i data-lucide="link" className="w-5 h-5"></i>}
|
|
label="Parámetro de Inicio"
|
|
subValue="Usado para referencias/invitaciones"
|
|
value={data.session.start_param}
|
|
/>
|
|
|
|
{/* SECCIÓN 2: Entorno Técnico */}
|
|
<SectionTitle title="Dispositivo y App" />
|
|
<InfoCard
|
|
icon={<i data-lucide="smartphone" className="w-5 h-5"></i>}
|
|
label="Plataforma"
|
|
value={formatPlatform(data.device.platform)}
|
|
/>
|
|
<InfoCard
|
|
icon={<i data-lucide="git-branch" className="w-5 h-5"></i>}
|
|
label="Versión Telegram"
|
|
value={`v${data.device.version}`}
|
|
/>
|
|
<InfoCard
|
|
icon={<i data-lucide="palette" className="w-5 h-5"></i>}
|
|
label="Tema del Sistema"
|
|
value={data.device.colorScheme === 'dark' ? '🌙 Oscuro' : '☀️ Claro'}
|
|
/>
|
|
|
|
{/* SECCIÓN 3: Seguridad de Sesión */}
|
|
<SectionTitle title="Sesión" />
|
|
<InfoCard
|
|
icon={<i data-lucide="clock" className="w-5 h-5"></i>}
|
|
label="Fecha de Autenticación"
|
|
value={<span className="text-xs">{formatDate(data.session.auth_date)}</span>}
|
|
/>
|
|
|
|
{/* Botón de acción */}
|
|
<div className="mt-8 mb-4">
|
|
<button
|
|
onClick={() => window.Telegram.WebApp.close()}
|
|
className="w-full bg-slate-800 hover:bg-slate-700 text-slate-300 font-medium py-3.5 px-4 rounded-xl transition-all border border-slate-700 flex items-center justify-center gap-2 active:scale-95"
|
|
>
|
|
<i data-lucide="x" className="w-4 h-4"></i>
|
|
Cerrar Mini App
|
|
</button>
|
|
</div>
|
|
|
|
<div className="text-center">
|
|
<p className="text-[10px] text-slate-600">
|
|
Mini App v1.2 • React + Tailwind
|
|
</p>
|
|
</div>
|
|
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
|
root.render(<App />);
|
|
</script>
|
|
</body>
|
|
</html> |