Advertisement
BuilderGaming

Untitled

Jun 29th, 2025
477
0
18 hours
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import { useState, useEffect } from 'react';
  2. import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
  3. import {
  4.   faCoins,
  5.   faTicket,
  6.   faUsers,
  7.   faInfo,
  8.   faList,
  9.   faTrophy
  10. } from '@fortawesome/free-solid-svg-icons';
  11. import { call, listen } from "@autumngmod/cream-api"
  12.  
  13. const LotteryTab = () => {
  14.   type Entry = {
  15.     player_name: string;
  16.     tickets: number
  17.   };
  18.  
  19.   type Winner = {
  20.     player_name: string;
  21.     jackpot_won: number;
  22.     date_won: number; // unix timestamp
  23.   };
  24.  
  25.   const [localPlySteamId, setLocalPlySteamId] = useState<string | null>(null);
  26.   const [ticketsToBuy, setTicketsToBuy] = useState(0);
  27.   const [jackpot, setJackpot] = useState(0);
  28.   const [entries, setEntries] = useState<Entry[]>([]);
  29.   const [winners, setWinners] = useState<Winner[]>([]);
  30.   const [loading, setLoading] = useState(true);
  31.   const [error, setError] = useState<string | null>(null);
  32.  
  33.   // Fetch lottery data
  34.   const fetchAndSetLotteryData = async () => {
  35.     try {
  36.       const baseUrl = 'https://giantslair.com';
  37.       const response = await fetch(`${baseUrl}/gl/lottery/dumpjson`, {
  38.         method: "GET",
  39.         headers: {
  40.           'Accept': 'application/json',
  41.         },
  42.       });
  43.       if (!response.ok) {
  44.         throw new Error(`HTTP error! status: ${response.status}`);
  45.       }
  46.       const contentType = response.headers.get('content-type');
  47.       if (!contentType || !contentType.includes('application/json')) {
  48.         throw new Error('API response is not JSON');
  49.       }
  50.       const data = await response.json();
  51.       if (!data.entries || !data.history_winners || data.current_jackpot === undefined) {
  52.         throw new Error('Invalid API response structure');
  53.       }
  54.       setJackpot(data.current_jackpot);
  55.       setEntries(data.entries);
  56.       setWinners(data.history_winners);
  57.       setError(null);
  58.       setLoading(false);
  59.       // console.log('Lottery data fetched successfully:', { jackpot: data.current_jackpot, entries: data.entries, winners: data.history_winners });
  60.     } catch (err) {
  61.       console.error('Failed to fetch lottery data:', err);
  62.       setError(err instanceof Error ? err.message : String(err));
  63.       setLoading(false);
  64.     }
  65.   };
  66.  
  67.   // Fetch lottery data
  68.   useEffect(() => {
  69.     const fetchLotteryData = async () => {
  70.       try {
  71.         const baseUrl = 'https://giantslair.com';
  72.         const response = await fetch(`${baseUrl}/gl/lottery/dumpjson`, {
  73.           method: "GET",
  74.           headers: {
  75.             'Accept': 'application/json',
  76.           },
  77.         });
  78.        
  79.         if (!response.ok) {
  80.           throw new Error(`HTTP error! status: ${response.status}`);
  81.         }
  82.  
  83.        
  84.         if (!response.ok) {
  85.           throw new Error(`HTTP error! status: ${response.status}`);
  86.         }
  87.        
  88.         const contentType = response.headers.get('content-type');
  89.         if (!contentType || !contentType.includes('application/json')) {
  90.           throw new Error('API response is not JSON');
  91.         }
  92.        
  93.         const data = await response.json();
  94.        
  95.         // Validate response structure
  96.         if (!data.entries || !data.history_winners || data.current_jackpot === undefined) {
  97.           throw new Error('Invalid API response structure');
  98.         }
  99.        
  100.         setJackpot(data.current_jackpot);
  101.         setEntries(data.entries);
  102.         setWinners(data.history_winners);
  103.         setError(null);
  104.  
  105.         // console.log('Lottery data fetched successfully:', { jackpot: data.current_jackpot, entries: data.entries, winners: data.history_winners });
  106.       } catch (err) {
  107.         console.error('Failed to fetch lottery data:', err);
  108.         setError(err instanceof Error ? err.message : String(err));
  109.       } finally {
  110.         setLoading(false);
  111.       }
  112.     };
  113.  
  114.     const getLocalPlySteamId = async () => {
  115.       const steamid = await call("getLocalPlySteamId", 0);
  116.       setLocalPlySteamId(String(steamid));
  117.     };
  118.  
  119.     fetchLotteryData();
  120.     getLocalPlySteamId();
  121.   }, []);
  122.  
  123.   listen("refreshLotteryTab", () => {
  124.     setLoading(true);
  125.     fetchAndSetLotteryData();
  126.     setTicketsToBuy(0);
  127.   });
  128.  
  129.   // Calculate derived data
  130.   const totalTickets = jackpot / 100; // Match Lua logic: total tickets in pool = jackpot / 100
  131.   const playersEntered = entries.length;
  132.  
  133.   // Find our entry if we have tickets in the pool
  134.   const ourEntry = localPlySteamId
  135.     ? entries.find((entry: any) => entry.steamid === localPlySteamId)
  136.     : undefined;
  137.   const ourCurrentTickets = ourEntry ? ourEntry.tickets : 0;
  138.  
  139.   // Calculate user's chance based on current and to-buy tickets
  140.   const newTotalTickets = totalTickets + ticketsToBuy;
  141.   const yourTotalTickets = ourCurrentTickets + ticketsToBuy;
  142.   const yourChance = newTotalTickets > 0
  143.     ? Math.min((yourTotalTickets / newTotalTickets) * 100, 100)
  144.     : 100;
  145.  
  146.   if (loading) {
  147.     return (
  148.       <div className="flex justify-center items-center h-64">
  149.         <div className="text-gl-primary text-xl">Loading lottery data...</div>
  150.       </div>
  151.     );
  152.   }
  153.  
  154.   if (error) {
  155.     return (
  156.       <div className="bg-red-900/30 border-l-4 border-red-500 p-4 rounded-r-lg my-8">
  157.         <div className="flex items-start gap-2">
  158.           <div className="w-5 h-5 bg-red-500 rounded-full flex items-center justify-center flex-shrink-0 mt-0.5">
  159.             <FontAwesomeIcon icon={faInfo} className="text-white text-xs" />
  160.           </div>
  161.           <p className="text-red-200 text-sm leading-relaxed">
  162.             Error loading lottery data: {error}
  163.           </p>
  164.         </div>
  165.       </div>
  166.     );
  167.   }
  168.  
  169.   return (
  170.     <div className="scrollable-content h-full">
  171.       <div className="grid grid-cols-1 lg:grid-cols-3 gap-8 p-8">
  172.         {/* Current Jackpot Section */}
  173.         <div className="bg-gray-800/50 rounded-lg p-6 lg:col-span-1">
  174.           <div className="flex items-center gap-3 mb-4">
  175.             <div className="w-8 h-8 rounded-lg flex items-center justify-center">
  176.               <FontAwesomeIcon icon={faCoins} className="w-6 h-6 text-gl-primary" />
  177.             </div>
  178.             <h2 className="text-xl font-semibold text-gray-300">Current Jackpot</h2>
  179.           </div>
  180.          
  181.           <div className="text-6xl font-bold text-gl-primary mb-2">
  182.             {jackpot.toLocaleString()}
  183.           </div>
  184.           <div className="text-gray-400 mb-8">Points</div>
  185.          
  186.           <div className="grid grid-cols-2 gap-6">
  187.             <div className="text-center">
  188.               <div className="w-12 h-12 bg-gl-secondary-light/20 rounded-lg flex items-center justify-center mx-auto mb-2">
  189.                 <FontAwesomeIcon icon={faTicket} className="w-8 h-8 text-gl-primary" />
  190.               </div>
  191.               <div className="text-2xl font-bold text-white">{totalTickets.toLocaleString()}</div>
  192.               <div className="text-sm text-gray-400">Tickets in Pool</div>
  193.             </div>
  194.            
  195.             <div className="text-center">
  196.               <div className="w-12 h-12 bg-gl-secondary-light/20 rounded-lg flex items-center justify-center mx-auto mb-2">
  197.                 <FontAwesomeIcon icon={faUsers} className="w-8 h-8 text-gl-primary" />
  198.               </div>
  199.               <div className="text-2xl font-bold text-white">{playersEntered.toLocaleString()}</div>
  200.               <div className="text-sm text-gray-400">Players Entered</div>
  201.             </div>
  202.           </div>
  203.         </div>
  204.  
  205.         {/* Purchase Tickets Section */}
  206.         <div className="bg-gray-800/50 rounded-lg p-6 flex flex-col lg:col-span-2">
  207.           <div className="flex items-center gap-3 mb-4">
  208.             <div className="w-8 h-8 rounded-lg flex items-center justify-center">
  209.               <FontAwesomeIcon icon={faTicket} className="w-6 h-6 text-gl-primary" />
  210.             </div>
  211.             <h2 className="text-xl font-semibold text-gray-300">Purchase Tickets</h2>
  212.           </div>
  213.          
  214.           <div className="grid grid-cols-2 gap-4 mb-6">
  215.             <div>
  216.               <label className="flex text-sm text-gray-400 mb-2">Your Chance</label>
  217.               <span className="text-2xl font-bold text-gl-primary">{yourChance.toFixed(2)}%</span>
  218.             </div>
  219.            
  220.             <div>
  221.               <label className="flex text-sm text-gray-400 mb-2">Tickets to Buy</label>
  222.               <div className="flex gap-2">
  223.                 <div className="bg-gray-700 rounded-lg px-4 py-3 text-center flex-1">
  224.                   <input
  225.                     type="number"
  226.                     value={ticketsToBuy}
  227.                     onChange={(e) => setTicketsToBuy(Math.max(0, parseInt(e.target.value) || 0))}
  228.                     className="bg-transparent text-2xl font-bold text-white text-center w-full outline-none"
  229.                     min="0"
  230.                   />
  231.                 </div>
  232.                 <div className="flex">
  233.                   <button
  234.                     onClick={() => setTicketsToBuy(prev => prev + 1)}
  235.                     className="bg-gray-600 hover:bg-gray-500 text-white text-sm px-3 py-2 rounded-l-lg border-r border-gray-500 transition-colors"
  236.                   >
  237.                     +1
  238.                   </button>
  239.                   <button
  240.                     onClick={() => setTicketsToBuy(prev => prev + 5)}
  241.                     className="bg-gray-600 hover:bg-gray-500 text-white text-sm px-3 py-2 border-r border-gray-500 transition-colors"
  242.                   >
  243.                     +5
  244.                   </button>
  245.                   <button
  246.                     onClick={() => setTicketsToBuy(prev => prev + 10)}
  247.                     className="bg-gray-600 hover:bg-gray-500 text-white text-sm px-3 py-2 border-r border-gray-500 transition-colors"
  248.                   >
  249.                     +10
  250.                   </button>
  251.                   <button
  252.                     onClick={() => setTicketsToBuy(0)}
  253.                     className="bg-gray-600 hover:bg-gray-500 text-white text-sm px-3 py-2 rounded-r-lg transition-colors"
  254.                   >
  255.                     Reset
  256.                   </button>
  257.                 </div>
  258.               </div>
  259.             </div>
  260.           </div>
  261.  
  262.           <div className="mb-6">
  263.             <button
  264.               onClick={async () => {
  265.                 try {
  266.                   await call("purchaseLotteryTickets", ticketsToBuy);
  267.                 } catch (error) {
  268.                   console.error('Error purchasing tickets:', error);
  269.                 }
  270.               }}
  271.               disabled={ticketsToBuy <= 0}
  272.               className="bg-gl-primary hover:bg-gl-primary-hover disabled:bg-gray-600 disabled:cursor-not-allowed text-white font-bold py-3 px-6 rounded-lg transition-colors"
  273.             >
  274.               Purchase {ticketsToBuy} Ticket{ticketsToBuy !== 1 ? 's' : ''} ({(ticketsToBuy * 100).toLocaleString()} points)
  275.             </button>
  276.           </div>
  277.          
  278.           <div className="mt-auto">
  279.             <div className="border-l-4 border-gl-secondary bg-gl-secondary/10 p-4 rounded-r-lg">
  280.               <div className="flex items-start gap-2">
  281.                 <div className="w-5 h-5 bg-gl-secondary rounded-full flex items-center justify-center flex-shrink-0 mt-0.5">
  282.                   <FontAwesomeIcon icon={faInfo} className="text-white text-sm" />
  283.                 </div>
  284.                 <p className="text-sm leading-relaxed text-gray-300">
  285.                   Lottery drawings are picked every day at <span className="text-gl-primary font-semibold">11:59PM EST</span> to determine a winner. One ticket is
  286.                   randomly picked and the winner is awarded the entire jackpot for the given day.
  287.                 </p>
  288.               </div>
  289.             </div>
  290.           </div>
  291.         </div>
  292.  
  293.         {/* Current Entries and Last 30 Days Winners Tables */}
  294.         <div className="lg:col-span-3 grid grid-cols-1 lg:grid-cols-2 gap-8">
  295.           {/* Current Entries Table */}
  296.           <div className="bg-gray-800/50 rounded-lg p-6 flex flex-col h-full min-h-0">
  297.             <h3 className="text-xl font-semibold text-gray-300 mb-6 flex items-center gap-3">
  298.               <span className="w-8 h-8 rounded-lg flex items-center justify-center">
  299.                 <FontAwesomeIcon icon={faList} className="w-6 h-6 text-gl-primary" />
  300.               </span>
  301.               Current Entries
  302.             </h3>
  303.             <div className="space-y-4 scrollable-content flex-1 min-h-0" style={{maxHeight: 'calc(100vh - 70vh)'}}>
  304.               <div className="flex justify-between items-center text-sm text-gray-400 pb-2 border-b border-gray-700">
  305.                 <span>Player</span>
  306.                 <span>Tickets Bought</span>
  307.               </div>
  308.              
  309.               {entries.map((entry, index) => (
  310.                 <div key={index} className="flex justify-between items-center py-2">
  311.                   <div className="flex items-center gap-3">
  312.                     <div className="w-8 h-8 bg-gl-primary rounded-full flex items-center justify-center">
  313.                       <span className="text-sm font-medium text-white">
  314.                         {entry.player_name.charAt(0).toUpperCase()}
  315.                       </span>
  316.                     </div>
  317.                     <span className="text-white">{entry.player_name}</span>
  318.                   </div>
  319.                   <span className="text-gl-primary font-medium">
  320.                     {entry.tickets.toLocaleString()}
  321.                   </span>
  322.                 </div>
  323.               ))}
  324.              
  325.               {entries.length === 0 && (
  326.                 <div className="text-center py-8 text-gray-400">
  327.                   No entries yet. Be the first to buy tickets!
  328.                 </div>
  329.               )}
  330.             </div>
  331.           </div>
  332.  
  333.           {/* Last 30 Days Winners Table */}
  334.           <div className="bg-gray-800/50 rounded-lg p-6 flex flex-col h-full min-h-0">
  335.             <h3 className="text-xl font-semibold text-gray-300 mb-6 flex items-center gap-3">
  336.               <span className="w-8 h-8 rounded-lg flex items-center justify-center">
  337.                 <FontAwesomeIcon icon={faTrophy} className="w-6 h-6 text-gl-primary" />
  338.               </span>
  339.               Last 30 Days Winners
  340.             </h3>
  341.             <div className="space-y-4 scrollable-content flex-1 min-h-0" style={{maxHeight: 'calc(100vh - 350px)'}}>
  342.               <div className="flex justify-between items-center text-sm text-gray-400 pb-2 border-b border-gray-700">
  343.                 <span>Player</span>
  344.                 <span>Points Won</span>
  345.                 <span>Date</span>
  346.               </div>
  347.              
  348.               {winners.map((winner, index) => (
  349.                 <div key={index} className="flex justify-between items-center py-2">
  350.                   <div className="flex items-center gap-3">
  351.                     <div className="w-8 h-8 bg-yellow-500 rounded-full flex items-center justify-center">
  352.                       <FontAwesomeIcon icon={faTrophy} className="text-yellow-900 text-xs" />
  353.                     </div>
  354.                     <span className="text-white">{winner.player_name}</span>
  355.                   </div>
  356.                   <span className="text-gl-primary font-medium">
  357.                     {winner.jackpot_won.toLocaleString()}
  358.                   </span>
  359.                   <span className="text-gray-400 text-sm">{
  360.                     new Date(winner.date_won * 1000).toLocaleDateString('en-US', {
  361.                       year: 'numeric', month: '2-digit', day: '2-digit'
  362.                     })
  363.                   }</span>
  364.                 </div>
  365.               ))}
  366.              
  367.               {winners.length === 0 && (
  368.                 <div className="text-center py-8 text-gray-400">
  369.                   No recent winners to display.
  370.                 </div>
  371.               )}
  372.             </div>
  373.           </div>
  374.         </div>
  375.       </div>
  376.     </div>
  377.   );
  378. };
  379.  
  380. export default LotteryTab;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement