+
PFP's
@@ -93,7 +95,7 @@ export const NFTGrid: React.FC = ({
My Online Appearances
-
+
{nfts.map((nft) => (
= ({
))}
-
+
);
};
\ No newline at end of file
diff --git a/src/components/ReactorStatus/index.tsx b/src/components/ReactorStatus/index.tsx
new file mode 100644
index 0000000..5197cb2
--- /dev/null
+++ b/src/components/ReactorStatus/index.tsx
@@ -0,0 +1,273 @@
+import { useState, useEffect } from 'react';
+import { Activity, Power, Atom, Coffee, TrendingUp, Wallet } from 'lucide-react';
+import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';
+import type { ReactorStatusProps, TooltipProps, PriceStat } from './types';
+import { getEthereumData, formatPrice, getHighLowPrices, formatVolume } from '../../utils/api';
+
+const statusMessages = [
+ 'Staking valves open',
+ 'Consensus achieved',
+ 'Smart contracts cooling',
+ 'Tokens well-contained',
+ 'DAOs running smoothly',
+ 'Radiation levels nominal',
+ 'Governance protocols stable',
+ 'Core temperature optimal',
+ 'NFTs properly shielded',
+ 'Blockchain fully operational'
+];
+
+const CustomTooltip = ({ active, payload, label }: TooltipProps) => {
+ if (active && payload && payload.length && label) {
+ return (
+
+
{formatPrice(payload[0].value)}
+
+ {new Date(parseInt(label)).toLocaleTimeString()}
+
+
+ );
+ }
+ return null;
+};
+
+const ReactorStatus = ({ className = '' }: ReactorStatusProps) => {
+ const [power, setPower] = useState(0);
+ const [status, setStatus] = useState('Initializing...');
+ const [isAnimating, setIsAnimating] = useState(false);
+ const [currentTime, setCurrentTime] = useState(new Date());
+ const [priceData, setPriceData] = useState
>([]);
+ const [priceStats, setPriceStats] = useState([
+ { label: '24h High', value: '...' },
+ { label: '24h Low', value: '...' },
+ { label: '24h Volume', value: '...' }
+ ]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ setLoading(true);
+ setError(null);
+
+ const data = await getEthereumData();
+ console.log('Received ETH data:', data);
+
+ if (!data?.prices?.length) {
+ throw new Error('Invalid price data received');
+ }
+
+ const formattedPrices = data.prices.map(([timestamp, price]) => ({
+ time: timestamp,
+ price
+ }));
+
+ const { high, low } = getHighLowPrices(data.prices);
+ const volume = data.total_volumes[data.total_volumes.length - 1][1];
+
+ console.log('Formatted prices:', formattedPrices);
+ setPriceData(formattedPrices);
+ setPriceStats([
+ { label: '24h High', value: formatPrice(high) },
+ { label: '24h Low', value: formatPrice(low) },
+ { label: '24h Volume', value: formatVolume(volume) }
+ ]);
+ } catch (err) {
+ console.error('Error fetching ETH data:', err);
+ setError(err instanceof Error ? err.message : 'Failed to fetch price data');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // Initial fetch
+ fetchData();
+
+ // Set up intervals
+ const intervals = [
+ setInterval(() => setCurrentTime(new Date()), 1000),
+ setInterval(() => {
+ setPower(prev => {
+ const fluctuation = Math.random() * 10 - 5;
+ return Math.min(Math.max(prev + fluctuation, 60), 100);
+ });
+ }, 3000),
+ setInterval(() => {
+ const randomStatus = statusMessages[Math.floor(Math.random() * statusMessages.length)];
+ setStatus(randomStatus);
+ setIsAnimating(true);
+ setTimeout(() => setIsAnimating(false), 500);
+ }, 5000),
+ setInterval(fetchData, 300000) // Fetch new data every 5 minutes
+ ];
+
+ // Cleanup
+ return () => intervals.forEach(clearInterval);
+ }, []);
+
+ const statusIndicators = [
+ { label: 'Core Temp', value: '37.2°C' },
+ { label: 'Block Height', value: '#18M' },
+ { label: 'Rad Level', value: '0.12 μSv' }
+ ];
+
+ const renderPriceChart = () => {
+ if (loading) {
+ return (
+
+
+
+
Loading price data...
+
+
+ );
+ }
+
+ if (error) {
+ return (
+
+ );
+ }
+
+ if (!priceData.length) {
+ return (
+
+
No price data available
+
+ );
+ }
+
+ return (
+
+
+ new Date(timestamp).toLocaleTimeString()}
+ />
+ formatPrice(value)}
+ />
+ } />
+
+
+
+ );
+ };
+
+ return (
+
+ {/* Reactor Status Panel */}
+
+
+
+
+
+
+ {currentTime.toLocaleTimeString()}
+
+
+
+
+
+ {/* Power Level */}
+
+
+
+
+ {power.toFixed(1)}% Capacity
+
+
+
+ {/* Status Message */}
+
+
+ Current Status
+
+
+
+ {status}
+
+
+
+ {/* Activity Indicators */}
+
+ {statusIndicators.map((item, index) => (
+
+
{item.label}
+
{item.value}
+
+ ))}
+
+
+
+
+ {/* ETH Price Monitor */}
+
+
+
+
+
+
ETH Price Monitor
+
+
+
+ Live Feed
+
+
+ {priceData.length > 0 && (
+
+
Current Price
+
+ {formatPrice(priceData[priceData.length - 1].price)}
+
+
+ )}
+
+
+
+ {/* Price Chart */}
+
+ {renderPriceChart()}
+
+
+ {/* Price Stats */}
+
+ {priceStats.map((item, index) => (
+
+
{item.label}
+
{item.value}
+
+ ))}
+
+
+
+
+ );
+};
+
+export default ReactorStatus;
\ No newline at end of file
diff --git a/src/components/ReactorStatus/types.ts b/src/components/ReactorStatus/types.ts
new file mode 100644
index 0000000..05113ca
--- /dev/null
+++ b/src/components/ReactorStatus/types.ts
@@ -0,0 +1,24 @@
+// src/components/ReactorStatus/types.ts
+
+export interface ReactorStatusProps {
+ className?: string;
+ }
+
+ export interface TooltipProps {
+ active?: boolean;
+ payload?: Array<{
+ value: number;
+ dataKey?: string;
+ }>;
+ label?: string;
+ }
+
+ export interface PriceStat {
+ label: string;
+ value: string;
+ }
+
+ export interface PriceDataPoint {
+ time: number;
+ price: number;
+ }
\ No newline at end of file
diff --git a/src/components/Skills/index.tsx b/src/components/Skills/index.tsx
index c2fb57d..d234e18 100644
--- a/src/components/Skills/index.tsx
+++ b/src/components/Skills/index.tsx
@@ -3,20 +3,20 @@ import { ChevronRight } from 'lucide-react';
import type { SkillsProps, SkillCardProps, SkillBarProps, Skill } from './types';
const SkillBar: FC = ({
- level,
- maxLevel = 5,
- className = ''
- }) => {
- const percentage = (level / maxLevel) * 100;
- return (
-
- );
- };
+ level,
+ maxLevel = 5,
+ className = ''
+}) => {
+ const percentage = (level / maxLevel) * 100;
+ return (
+
+ );
+};
const SkillCard: FC = ({
skill,
@@ -114,25 +114,25 @@ const defaultSkills: Skill[] = [
];
export const Skills: FC = ({
- className = '',
- skills = defaultSkills
- }) => {
- return (
-
-
-
-
- Professional Skills
-
-
-
- {skills.map((skill, index) => (
-
- ))}
-
+ className = '',
+ skills = defaultSkills
+}) => {
+ return (
+
+
+
+
+ Professional Skills
+
-
- );
- };
+
+ {skills.map((skill, index) => (
+
+ ))}
+
+
+
+ );
+};
export default Skills;
\ No newline at end of file
diff --git a/src/utils/api.ts b/src/utils/api.ts
new file mode 100644
index 0000000..4e84268
--- /dev/null
+++ b/src/utils/api.ts
@@ -0,0 +1,76 @@
+// src/utils/api.ts
+
+const API_KEY = import.meta.env.VITE_CG_API_KEY;
+
+export interface PriceData {
+ prices: [number, number][];
+ market_caps: [number, number][];
+ total_volumes: [number, number][];
+}
+
+export async function getEthereumData(): Promise
{
+ try {
+ console.log('Fetching ETH price data...');
+ const response = await fetch(
+ `https://pro-api.coingecko.com/api/v3/coins/ethereum/market_chart?vs_currency=usd&days=1&interval=hourly&x_cg_pro_api_key=${API_KEY}`
+ );
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ console.error('API Error Response:', errorText);
+ throw new Error(`API request failed: ${response.status} ${response.statusText}`);
+ }
+
+ const data = await response.json();
+ console.log('Raw API response:', data);
+ return data;
+ } catch (error) {
+ console.error('Error in getEthereumData:', error);
+ // Return mock data in case of API failure
+ const now = Date.now();
+ const mockData: PriceData = {
+ prices: Array.from({ length: 24 }, (_, i) => {
+ const timestamp = now - (23 - i) * 3600000;
+ const basePrice = 2300;
+ const randomChange = Math.random() * 100 - 50;
+ return [timestamp, basePrice + randomChange] as [number, number];
+ }),
+ market_caps: Array.from({ length: 24 }, (_, i) => {
+ const timestamp = now - (23 - i) * 3600000;
+ return [timestamp, 2300000000] as [number, number];
+ }),
+ total_volumes: Array.from({ length: 24 }, (_, i) => {
+ const timestamp = now - (23 - i) * 3600000;
+ return [timestamp, 500000000] as [number, number];
+ })
+ };
+ return mockData;
+ }
+}
+
+export function formatPrice(price: number): string {
+ return new Intl.NumberFormat('en-US', {
+ style: 'currency',
+ currency: 'USD',
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2
+ }).format(price);
+}
+
+export function getHighLowPrices(prices: [number, number][]): { high: number; low: number } {
+ const priceValues = prices.map(([, price]) => price);
+ return {
+ high: Math.max(...priceValues),
+ low: Math.min(...priceValues)
+ };
+}
+
+export function formatVolume(volume: number): string {
+ if (volume >= 1e9) {
+ return `$${(volume / 1e9).toFixed(1)}B`;
+ }
+ if (volume >= 1e6) {
+ return `$${(volume / 1e6).toFixed(1)}M`;
+ }
+ return `$${volume.toFixed(0)}`;
+}
\ No newline at end of file