Files
LightningLotto/front_end/src/app/past-wins/page.tsx
Michilis 3bc067f691 feat: display truncated lightning address for winners
- Add truncateLightningAddress utility (shows first 2 chars + ******)
- Backend: Include winner_address in past-wins API response
- Frontend: Display truncated address in past winners list
- Telegram: Add truncated address to draw announcements for transparency

Example: username@blink.sv -> us******@blink.sv
2025-12-12 16:20:18 +00:00

125 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { useEffect, useState } from 'react';
import api from '@/lib/api';
import { LoadingSpinner } from '@/components/LoadingSpinner';
import STRINGS from '@/constants/strings';
import { formatDateTime } from '@/lib/format';
interface PastWin {
cycle_id: string;
cycle_type: string;
scheduled_at: string;
pot_total_sats: number;
pot_after_fee_sats: number | null;
winner_name: string;
winner_address: string | null;
winning_ticket_serial: number | null;
}
export default function PastWinsPage() {
const [wins, setWins] = useState<PastWin[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const loadWins = async () => {
try {
const response = await api.getPastWins();
setWins(response.data?.wins || []);
} catch (err: any) {
setError(err.message || 'Failed to load past wins');
} finally {
setLoading(false);
}
};
loadWins();
}, []);
if (loading) {
return <LoadingSpinner />;
}
if (error) {
return (
<div className="max-w-3xl mx-auto text-center py-12">
<div className="text-red-500 text-xl mb-2"> {error}</div>
<p className="text-gray-400">Please try again in a moment.</p>
</div>
);
}
return (
<div className="max-w-5xl mx-auto">
<div className="text-center mb-10">
<h1 className="text-3xl md:text-4xl font-bold text-white mb-3">
{STRINGS.pastWins.title}
</h1>
<p className="text-gray-400">{STRINGS.pastWins.description}</p>
</div>
{wins.length === 0 ? (
<div className="bg-gray-900 rounded-xl p-12 text-center border border-gray-800">
<div className="text-4xl mb-4"></div>
<div className="text-gray-300 text-lg">{STRINGS.pastWins.noWins}</div>
</div>
) : (
<div className="space-y-4">
{wins.map((win) => (
<div
key={win.cycle_id}
className="bg-gray-900 rounded-xl p-6 border border-gray-800"
>
<div className="flex flex-col md:flex-row md:items-center md:justify-between mb-4">
<div>
<div className="text-sm uppercase tracking-wide text-gray-500">
{win.cycle_type} {formatDateTime(win.scheduled_at)}
</div>
<div className="text-white font-mono text-sm">
{win.cycle_id.substring(0, 16)}...
</div>
</div>
<div className="mt-4 md:mt-0 text-right">
<div className="text-gray-400 text-sm">{STRINGS.pastWins.pot}</div>
<div className="text-2xl font-bold text-bitcoin-orange">
{win.pot_total_sats.toLocaleString()} sats
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 text-sm">
<div>
<div className="text-gray-400 mb-1">{STRINGS.pastWins.winner}</div>
<div className="text-white font-semibold">
{win.winner_name || 'Anon'}
</div>
</div>
<div>
<div className="text-gray-400 mb-1">{STRINGS.pastWins.address}</div>
<div className="text-white font-mono text-xs">
{win.winner_address || 'N/A'}
</div>
</div>
<div>
<div className="text-gray-400 mb-1">{STRINGS.pastWins.ticket}</div>
<div className="text-white">
{win.winning_ticket_serial !== null
? `#${win.winning_ticket_serial}`
: 'N/A'}
</div>
</div>
<div>
<div className="text-gray-400 mb-1">{STRINGS.pastWins.drawTime}</div>
<div className="text-white">{formatDateTime(win.scheduled_at)}</div>
</div>
</div>
</div>
))}
</div>
)}
</div>
);
}