Function bodies 642 total
RegisterPage function · typescript · L15-L246 (232 LOC)app/auth/register/page.tsx
export default function RegisterPage() {
const router = useRouter();
useEffect(() => {
if (process.env.NODE_ENV === 'production') {
router.replace('/');
}
}, [router]);
const [displayName, setDisplayName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
const [oauthLoading, setOauthLoading] = useState<string | null>(null);
const [error, setError] = useState('');
const [success, setSuccess] = useState(false);
async function handleRegister(e: React.FormEvent) {
e.preventDefault();
setLoading(true);
setError('');
try {
const res = await fetch('/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: email.toLowerCase().trim(),
password,
displayName: displayName.trim(),
}),
});
const data = await handleRegister function · typescript · L32-L68 (37 LOC)app/auth/register/page.tsx
async function handleRegister(e: React.FormEvent) {
e.preventDefault();
setLoading(true);
setError('');
try {
const res = await fetch('/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: email.toLowerCase().trim(),
password,
displayName: displayName.trim(),
}),
});
const data = await res.json();
if (!res.ok) {
setError(data.error || 'Something went wrong.');
setLoading(false);
return;
}
setSuccess(true);
await signIn('credentials', {
email: email.toLowerCase().trim(),
password,
callbackUrl: '/discuss',
redirect: true,
});
} catch {
setError('Something went wrong. Please try again.');
setLoading(false);
}
}handleOAuthLogin function · typescript · L70-L73 (4 LOC)app/auth/register/page.tsx
function handleOAuthLogin(providerId: string) {
setOauthLoading(providerId);
signIn(providerId, { callbackUrl: '/discuss' });
}GoogleIcon function · typescript · L248-L257 (10 LOC)app/auth/register/page.tsx
function GoogleIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" />
<path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" />
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" />
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" />
</svg>
);
}GitHubIcon function · typescript · L259-L265 (7 LOC)app/auth/register/page.tsx
function GitHubIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0 1 12 6.844a9.59 9.59 0 0 1 2.504.337c1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.02 10.02 0 0 0 22 12.017C22 6.484 17.522 2 12 2z" />
</svg>
);
}DiscordIcon function · typescript · L267-L273 (7 LOC)app/auth/register/page.tsx
function DiscordIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.15SectionCard function · typescript · L73-L94 (22 LOC)app/blog/iran-tensions-metals-squeeze/page.tsx
function SectionCard({ icon: Icon, iconColor, title, children }: {
icon: React.ElementType;
iconColor: string;
title: string;
children: React.ReactNode;
}) {
return (
<div className="mb-16 mt-20">
<div className="flex items-center gap-2.5 mb-12">
<div className={`w-8 h-8 rounded-lg flex items-center justify-center ${iconColor}`}>
<Icon className="w-4 h-4 text-white" />
</div>
<h2 className="text-lg sm:text-xl font-bold text-slate-900 dark:text-white">
{title}
</h2>
</div>
<div className="text-slate-600 dark:text-slate-300 leading-loose space-y-6">
{children}
</div>
</div>
);
}Open data scored by Repobility · https://repobility.com
KeyPoint function · typescript · L96-L108 (13 LOC)app/blog/iran-tensions-metals-squeeze/page.tsx
function KeyPoint({ label, children }: { label: string; children: React.ReactNode }) {
return (
<div className="flex gap-3 p-3 sm:p-4 ml-1 sm:ml-6">
<span className="shrink-0 mt-0.5 w-5 h-5 rounded-full bg-amber-100 dark:bg-amber-500/20 flex items-center justify-center">
<span className="w-1.5 h-1.5 rounded-full bg-amber-500" />
</span>
<div>
<span className="font-semibold text-slate-900 dark:text-white">{label}: </span>
<span>{children}</span>
</div>
</div>
);
}Cite function · typescript · L110-L116 (7 LOC)app/blog/iran-tensions-metals-squeeze/page.tsx
function Cite({ children }: { children: React.ReactNode }) {
return (
<span className="text-amber-600 dark:text-amber-400 text-sm font-medium">
({children})
</span>
);
}P function · typescript · L118-L124 (7 LOC)app/blog/iran-tensions-metals-squeeze/page.tsx
function P({ children }: { children: React.ReactNode }) {
return (
<p style={{ textIndent: '2em' }} className="text-slate-600 dark:text-slate-300 leading-loose">
{children}
</p>
);
}BlogIndex function · typescript · L115-L257 (143 LOC)app/blog/page.tsx
export default function BlogIndex() {
const featured = BLOG_POSTS.find((p) => p.featured);
const rest = BLOG_POSTS.filter((p) => !p.featured);
return (
<div className="min-h-screen bg-slate-50 dark:bg-slate-950 flex flex-col">
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(blogJsonLd) }}
/>
{/* Header */}
<div className="border-b border-slate-200 dark:border-slate-800 bg-white/50 dark:bg-black/30">
<div className="max-w-4xl mx-auto px-6 py-4">
<Link
href="/"
className="inline-flex items-center gap-2 text-sm font-medium text-slate-500 hover:text-slate-900 dark:hover:text-white transition-colors"
>
<ArrowLeft className="w-4 h-4" />
Back to Dashboard
</Link>
</div>
</div>
{/* Main Content */}
<main className="flex-1 w-full px-6 py-12 flex flex-col items-center">
<h1 className="text-Contact function · typescript · L25-L100 (76 LOC)app/contact/page.tsx
export default function Contact() {
return (
<div className="min-h-screen bg-slate-50 dark:bg-slate-950 flex flex-col">
<div className="flex-1 flex items-center justify-center py-16 px-8 lg:px-24">
<div className="max-w-4xl w-full">
<Link
href="/"
className="inline-flex items-center gap-2 text-sm text-slate-500 hover:text-slate-900 dark:hover:text-white transition-colors mb-16"
>
<ArrowLeft className="w-4 h-4" />
Back to Dashboard
</Link>
<div className="mb-16">
<h1 className="text-4xl md:text-5xl font-black tracking-tighter text-slate-900 dark:text-white mb-6">
Contact Us
</h1>
<p className="text-lg text-slate-500 dark:text-slate-400">
Have a question, suggestion, or found a data issue? We'd love to hear from you.
</p>
</div>
<div className="grid gap-10">
<div>
DeliveryLearnPage function · typescript · L42-L323 (282 LOC)app/delivery/page.tsx
export default function DeliveryLearnPage() {
return (
<div className="min-h-screen bg-slate-50 dark:bg-slate-950 flex flex-col">
{/* Header */}
<div className="border-b border-slate-200 dark:border-slate-800 bg-white/50 dark:bg-black/30">
<div className="max-w-4xl mx-auto px-6 py-4">
<Link
href="/"
className="inline-flex items-center gap-2 text-sm font-medium text-slate-500 hover:text-slate-900 dark:hover:text-white transition-colors"
>
<ArrowLeft className="w-4 h-4" />
Back to Dashboard
</Link>
</div>
</div>
{/* Main Content */}
<main className="flex-1 w-full px-6 py-12 flex flex-col items-center">
{/* Title */}
<div className="flex items-center gap-3 mb-3">
<Truck className="w-8 h-8 text-emerald-500" />
<h1 className="text-4xl sm:text-5xl font-black tracking-tight text-slate-900 dark:text-white text-center">
DeveloperDashboard function · typescript · L20-L366 (347 LOC)app/developer/page.tsx
export default function DeveloperDashboard() {
const { data: session, status } = useSession();
const router = useRouter();
const [keys, setKeys] = useState<ApiKeyInfo[]>([]);
const [loading, setLoading] = useState(true);
const [newKeyRaw, setNewKeyRaw] = useState<string | null>(null);
const [copied, setCopied] = useState(false);
const [keyName, setKeyName] = useState('');
const [creating, setCreating] = useState(false);
const [usage, setUsage] = useState<Record<string, { keyName: string; prefix: string; daily: { date: string; count: number }[] }>>({});
const fetchKeys = useCallback(async () => {
try {
const res = await fetch('/api/developer/keys');
const data = await res.json();
if (data.success) setKeys(data.keys);
} catch (err) {
console.error('Failed to fetch keys:', err);
} finally {
setLoading(false);
}
}, []);
const fetchUsage = useCallback(async () => {
try {
const res = await fetch('/api/developerWidgetGenerator function · typescript · L49-L252 (204 LOC)app/developer/widgets/page.tsx
export default function WidgetGenerator() {
const [selected, setSelected] = useState(WIDGETS[0]);
const [width, setWidth] = useState(WIDGETS[0].defaultWidth);
const [height, setHeight] = useState(WIDGETS[0].defaultHeight);
const [paramValues, setParamValues] = useState<Record<string, string>>({});
const [theme, setTheme] = useState('system');
const [copied, setCopied] = useState(false);
const handleSelect = (widget: typeof WIDGETS[0]) => {
setSelected(widget);
setWidth(widget.defaultWidth);
setHeight(widget.defaultHeight);
setParamValues({});
};
const buildUrl = () => {
const params = new URLSearchParams();
if (theme !== 'system') params.set('theme', theme);
for (const p of selected.params) {
const val = paramValues[p.key] || p.default;
if (!val || val === 'All') continue;
params.set(p.key, val);
}
const qs = params.toString();
return `https://heavymetalstats.com${selected.path}${qs ? '?' + qs : ''}`;
};
Repobility (the analyzer behind this table) · https://repobility.com
DiscussLayout function · typescript · L16-L18 (3 LOC)app/discuss/layout.tsx
export default function DiscussLayout({ children }: { children: React.ReactNode }) {
return <>{children}</>;
}DiscussPage function · typescript · L17-L30 (14 LOC)app/discuss/page.tsx
export default async function DiscussPage() {
let categories: ForumCategory[] = [];
if (isDatabaseAvailable()) {
try {
const result = await getForumCategories();
categories = result as ForumCategory[];
} catch (error) {
console.error('Error fetching forum categories:', error);
}
}
return <ForumContent categories={categories} />;
}EmbedLayoutInner function · typescript · L6-L31 (26 LOC)app/embed/layout.tsx
function EmbedLayoutInner({ children }: { children: React.ReactNode }) {
const searchParams = useSearchParams();
const theme = searchParams.get('theme');
const themeClass = theme === 'dark' ? 'dark' : theme === 'light' ? '' : '';
return (
<div className={themeClass} style={{ margin: 0, padding: 0, overflow: 'hidden' }}>
<div className="min-h-screen flex flex-col bg-white dark:bg-slate-950">
<div className="flex-1 p-3">
{children}
</div>
<div className="px-3 py-2 border-t border-slate-200 dark:border-slate-800 bg-slate-50 dark:bg-slate-900 text-center">
<a
href="https://heavymetalstats.com"
target="_blank"
rel="noopener noreferrer"
className="text-xs font-medium text-slate-500 hover:text-amber-600 dark:hover:text-amber-400 transition-colors"
>
Powered by HeavyMetalStats.com
</a>
</div>
</div>
</div>
);
}EmbedLayout function · typescript · L33-L39 (7 LOC)app/embed/layout.tsx
export default function EmbedLayout({ children }: { children: React.ReactNode }) {
return (
<Suspense>
<EmbedLayoutInner>{children}</EmbedLayoutInner>
</Suspense>
);
}EmbedPriceChartPage function · typescript · L22-L28 (7 LOC)app/embed/price-chart/page.tsx
export default function EmbedPriceChartPage() {
return (
<Suspense fallback={<div className="flex items-center justify-center h-full"><p className="text-sm text-slate-500 animate-pulse">Loading...</p></div>}>
<EmbedPriceChart />
</Suspense>
);
}EmbedPriceChart function · typescript · L30-L181 (152 LOC)app/embed/price-chart/page.tsx
function EmbedPriceChart() {
const searchParams = useSearchParams();
const metal = searchParams.get('metal') || 'Gold';
const days = searchParams.get('range') || '30';
const chartType = searchParams.get('chart') || 'area';
const accent = searchParams.get('accent') || '';
const showGrid = searchParams.get('grid') !== 'false';
const showZones = searchParams.get('zones') !== 'false';
const [data, setData] = useState<RiskDataPoint[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(`/api/risk-score/${metal}/history?days=${days}`)
.then(res => res.json())
.then(json => {
if (json.success && json.history) {
setData(json.history.map((h: { date: string; composite: number }) => ({
date: h.date,
composite: h.composite,
})));
}
})
.catch(err => console.error('Error fetching chart data:', err))
.finally(() => setLoading(false));
}, [metal, days]);
constEmbedRiskScoresPage function · typescript · L21-L27 (7 LOC)app/embed/risk-scores/page.tsx
export default function EmbedRiskScoresPage() {
return (
<Suspense fallback={<div className="flex items-center justify-center h-full"><p className="text-sm text-slate-500 animate-pulse">Loading...</p></div>}>
<EmbedRiskScores />
</Suspense>
);
}EmbedRiskScores function · typescript · L29-L64 (36 LOC)app/embed/risk-scores/page.tsx
function EmbedRiskScores() {
const searchParams = useSearchParams();
const style = searchParams.get('style') || 'bars';
const accent = searchParams.get('accent') || '';
const metals = searchParams.get('metals')?.split(',') || [];
const showLabels = searchParams.get('labels') !== 'false';
const [scores, setScores] = useState<Record<string, RiskScore>>({});
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/risk-score')
.then(res => res.json())
.then(json => {
if (json.success) setScores(json.scores);
})
.catch(err => console.error('Error fetching risk scores:', err))
.finally(() => setLoading(false));
}, []);
if (loading) {
return (
<div className="flex items-center justify-center py-12">
<p className="text-sm text-slate-500 animate-pulse">Loading risk scores...</p>
</div>
);
}
const filtered = metals.length > 0
? Object.entries(scores).filter(([m]) => metals.inIf a scraper extracted this row, it came from Repobility (https://repobility.com)
BarView function · typescript · L66-L103 (38 LOC)app/embed/risk-scores/page.tsx
function BarView({ entries, accent, showLabels }: { entries: [string, RiskScore][]; accent: string; showLabels: boolean }) {
return (
<div className="space-y-3 p-1">
<p className="text-[10px] font-bold uppercase tracking-widest text-slate-400 mb-1">COMEX Risk Scores</p>
{entries.map(([metal, score]) => {
const cfg = LEVEL_CONFIG[score.level] || FALLBACK;
const barColor = accent || cfg.color;
return (
<div key={metal} className="group">
<div className="flex items-center justify-between mb-1">
<span className="text-xs font-semibold text-slate-700 dark:text-slate-200">{metal}</span>
<div className="flex items-center gap-1.5">
{showLabels && (
<span className="text-[10px] font-medium uppercase tracking-wider" style={{ color: cfg.color }}>
{score.level}
</span>
)}
<span className="text-sm font-blacGaugeView function · typescript · L105-L146 (42 LOC)app/embed/risk-scores/page.tsx
function GaugeView({ entries, accent, showLabels }: { entries: [string, RiskScore][]; accent: string; showLabels: boolean }) {
return (
<div className="p-1">
<p className="text-[10px] font-bold uppercase tracking-widest text-slate-400 mb-3">COMEX Risk Scores</p>
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
{entries.map(([metal, score]) => {
const cfg = LEVEL_CONFIG[score.level] || FALLBACK;
const gaugeColor = accent || cfg.color;
const circumference = 2 * Math.PI * 36;
const dashoffset = circumference - (score.composite / 100) * circumference;
return (
<div key={metal} className="flex flex-col items-center">
<div className="relative w-20 h-20">
<svg viewBox="0 0 80 80" className="w-full h-full -rotate-90">
<circle cx="40" cy="40" r="36" fill="none" stroke="currentColor" strokeWidth="6" className="text-slate-200 dark:text-slate-800" />
CardView function · typescript · L148-L180 (33 LOC)app/embed/risk-scores/page.tsx
function CardView({ entries, accent, showLabels }: { entries: [string, RiskScore][]; accent: string; showLabels: boolean }) {
return (
<div className="p-1">
<p className="text-[10px] font-bold uppercase tracking-widest text-slate-400 mb-3">COMEX Risk Scores</p>
<div className="grid grid-cols-2 sm:grid-cols-3 gap-2">
{entries.map(([metal, score]) => {
const cfg = LEVEL_CONFIG[score.level] || FALLBACK;
const cardColor = accent || cfg.color;
return (
<div
key={metal}
className="p-3 rounded-lg border border-slate-200 dark:border-slate-800 bg-white dark:bg-slate-900"
style={{ borderLeftWidth: 3, borderLeftColor: cardColor }}
>
<div className="flex items-baseline justify-between">
<span className="text-xs font-semibold text-slate-700 dark:text-slate-200">{metal}</span>
<span className="text-lg font-black tabular-nums" style={{EmbedTickerPage function · typescript · L3-L5 (3 LOC)app/embed/ticker/page.tsx
export default function EmbedTickerPage() {
return <FuturesTicker />;
}RootLayout function · typescript · L162-L460 (299 LOC)app/layout.tsx
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
// Get the last updated timestamp from bulletin/delivery data (most reliable source)
let lastUpdatedText = 'Unknown'; // Default to Unknown when no data
let lastUpdatedISO = '';
try {
const bulletinData = (await import('../public/bulletin.json')).default as { parsed_date?: string; last_updated?: string };
const deliveryData = (await import('../public/delivery.json')).default as { parsed_date?: string; last_updated?: string };
const reportDateStr = deliveryData?.parsed_date || bulletinData?.parsed_date;
const timestampStr = bulletinData?.last_updated || deliveryData?.last_updated;
const dateSource = reportDateStr || (timestampStr ? timestampStr.split('T')[0] : undefined);
if (dateSource) {
const date = new Date(dateSource + 'T12:00:00');
if (!isNaN(date.getTime())) {
lastUpdatedText = date.toLocaleDateString('en-US', {
monLogoPreview function · typescript · L281-L402 (122 LOC)app/logo-preview/page.tsx
export default function LogoPreview() {
const [selected, setSelected] = useState(0);
const active = LOGOS[selected];
return (
<div className="min-h-screen bg-zinc-950 text-white">
{/* Live navbar preview */}
<div className="sticky top-0 z-50 w-full border-b border-white/[0.06] bg-zinc-950/80 backdrop-blur-xl">
<div className="w-full px-4 sm:px-6 lg:px-10">
<div className="flex items-center justify-between h-14">
<div className="flex items-center gap-2.5">
<div className={`w-8 h-8 rounded-lg ${active.bg} flex items-center justify-center shadow-sm p-1.5 transition-all duration-300`}>
{active.svg}
</div>
<span className="text-base font-black tracking-tight text-white uppercase">
Heavy Metal Stats
</span>
</div>
<div className="flex items-center gap-4 text-[11px] font-bold uppercase tracking-[0.15em] text-zinc-500">
manifest function · typescript · L3-L25 (23 LOC)app/manifest.ts
export default function manifest(): MetadataRoute.Manifest {
return {
name: 'COMEX Metals Inventory Tracker',
short_name: 'MetalsTracker',
description: 'Real-time supply and demand tracking for COMEX metals',
start_url: '/',
display: 'standalone',
background_color: '#000000',
theme_color: '#000000',
icons: [
{
src: '/icon.svg',
sizes: '64x64',
type: 'image/svg+xml',
},
{
src: '/favicon.ico',
sizes: 'any',
type: 'image/x-icon',
},
],
};
}Image function · typescript · L11-L233 (223 LOC)app/opengraph-image.tsx
export default async function Image() {
const currentDate = new Date().toLocaleDateString('en-US', {
month: 'long',
day: 'numeric',
year: 'numeric',
});
return new ImageResponse(
(
<div
style={{
height: '100%',
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#0f172a',
backgroundImage: 'linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%)',
}}
>
{/* Top accent line */}
<div
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '4px',
background: 'linear-gradient(90deg, #fbbf24, #f59e0b, #d97706)',
}}
/>
{/* Main content */}
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItePowered by Repobility — scan your code at https://repobility.com
formatOz function · typescript · L16-L20 (5 LOC)app/page.tsx
function formatOz(value: number): string {
if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}M`;
if (value >= 1_000) return `${(value / 1_000).toFixed(0)}K`;
return value.toFixed(0);
}getDataDate function · typescript · L22-L33 (12 LOC)app/page.tsx
function getDataDate(): string {
const dateStr = bulletinJson?.parsed_date || deliveryJson?.parsed_date
|| data?.Gold?.report_date;
if (!dateStr) return '';
try {
const d = dateStr.includes('/')
? new Date(parseInt(dateStr.split('/')[2]), parseInt(dateStr.split('/')[0]) - 1, parseInt(dateStr.split('/')[1]))
: new Date(dateStr + 'T12:00:00');
if (isNaN(d.getTime())) return '';
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
} catch { return ''; }
}generateMetadata function · typescript · L35-L61 (27 LOC)app/page.tsx
export function generateMetadata(): Metadata {
const silverReg = (data as Record<string, { totals?: { registered?: number } }>)?.Silver?.totals?.registered;
const goldReg = (data as Record<string, { totals?: { registered?: number } }>)?.Gold?.totals?.registered;
const dateLabel = getDataDate();
const silverStr = silverReg ? formatOz(silverReg) : null;
const goldStr = goldReg ? formatOz(goldReg) : null;
const title = 'Live Gold & Silver Prices, COMEX Inventory';
const description = silverStr && goldStr && dateLabel
? `Free precious metals dashboard — live prices, COMEX inventory (Silver ${silverStr} oz, Gold ${goldStr} oz as of ${dateLabel}), delivery data & forecasts.`
: 'Free precious metals dashboard with live gold & silver prices, COMEX warehouse inventory, coverage ratios, delivery data & price forecasts. Updated daily.';
return {
title,
description,
openGraph: {
title: `Live Gold & Silver Prices | COMEX Inventory — Heavy Metal Stats`,
Home function · typescript · L63-L163 (101 LOC)app/page.tsx
export default async function Home() {
// Try to fetch from database first (includes percent changes)
let dashboardData: WarehouseStocksData;
// Derive last updated date from when data was actually processed/published
// Priority: bulletin last_updated > delivery last_updated > bulletin parsed_date > warehouse report_date
let lastUpdatedText = 'Unknown';
const bulletinLastUpdated = bulletinJson?.last_updated;
const bulletinParsedDate = bulletinJson?.parsed_date;
const deliveryParsedDate = deliveryJson?.parsed_date;
const reportDate = data?.Gold?.report_date || data?.Silver?.report_date;
const reportDateStr = deliveryParsedDate || bulletinParsedDate;
const timestampStr = bulletinLastUpdated || (deliveryJson as Record<string, unknown>)?.last_updated as string | undefined;
const bestDateStr = reportDateStr || (timestampStr ? timestampStr.split('T')[0] : undefined);
if (bestDateStr) {
try {
const d = new Date(bestDateStr + 'T12:00:00');
if (!iPreciousMetalsPage function · typescript · L51-L352 (302 LOC)app/precious-metals/page.tsx
export default function PreciousMetalsPage() {
return (
<div className="min-h-screen bg-slate-50 dark:bg-slate-950 flex flex-col">
{/* Header */}
<div className="border-b border-slate-200 dark:border-slate-800 bg-white/50 dark:bg-black/30">
<div className="max-w-4xl mx-auto px-6 py-4">
<Link
href="/"
className="inline-flex items-center gap-2 text-sm font-medium text-slate-500 hover:text-slate-900 dark:hover:text-white transition-colors"
>
<ArrowLeft className="w-4 h-4" />
Back to Dashboard
</Link>
</div>
</div>
{/* Main Content */}
<main className="flex-1 w-full px-6 py-12 flex flex-col items-center">
{/* Title */}
<div className="flex items-center gap-3 mb-3">
<BarChart3 className="w-8 h-8 text-amber-500" />
<h1 className="text-4xl sm:text-5xl font-black tracking-tight text-slate-900 dark:text-white text-center">
PrivacyPolicy function · typescript · L25-L115 (91 LOC)app/privacy/page.tsx
export default function PrivacyPolicy() {
const sections = [
{
title: '1. Information Collection',
content: 'This application does not actively collect, store, or transmit personal information. No cookies, tracking pixels, or analytics scripts are embedded that collect personal data. No user accounts or login credentials are required to use this Service.',
},
{
title: '2. Data Sources',
content: 'All market data displayed is sourced from publicly available CME Group reports. No proprietary user data is incorporated into the analysis. The data presented includes warehouse inventory levels, delivery notices, and related market statistics.',
},
{
title: '3. Third-Party Services',
content: 'Links to external websites (CME Group, etc.) are provided for convenience. These third parties have their own privacy policies which we encourage you to review. We are not responsible for the privacy practices of external websites.',
},
{robots function · typescript · L5-L78 (74 LOC)app/robots.ts
export default function robots(): MetadataRoute.Robots {
return {
rules: [
{
userAgent: '*',
allow: '/',
disallow: [
'/private/',
'/api/',
'/_next/',
'/static/',
'/opengraph-image',
'/twitter-image',
'/auth/',
],
},
{
userAgent: 'Googlebot',
allow: '/',
disallow: [
'/api/',
'/opengraph-image',
'/twitter-image',
'/auth/',
],
},
{
userAgent: 'Googlebot-Image',
allow: [
'/',
'/opengraph-image',
'/twitter-image',
],
disallow: [
'/api/',
'/auth/',
],
},
{
userAgent: 'Bingbot',
allow: '/',
disallow: [
'/api/',
'/opengraph-image',
'/twitter-image',
'/auth/',
],
},
{
userAgent: 'DuckDuckBot',
sitemap function · typescript · L5-L93 (89 LOC)app/sitemap.ts
export default function sitemap(): MetadataRoute.Sitemap {
// Main pages with dynamic data (updates daily via GitHub Actions)
const mainPages: MetadataRoute.Sitemap = [
{
url: baseUrl,
lastModified: new Date(), // Updates daily with new COMEX data
changeFrequency: 'daily',
priority: 1.0,
},
];
// Educational/informational pages (high SEO value - keyword-rich content)
const infoPages: MetadataRoute.Sitemap = [
{
url: `${baseUrl}/precious-metals`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.95,
},
{
url: `${baseUrl}/learn`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.9,
},
{
url: `${baseUrl}/delivery`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.85,
},
{
url: `${baseUrl}/api-info`,
lastModified: new Date('2026-01-22'),
changeFrequency: 'monthly',
priority: 0.7,
Open data scored by Repobility · https://repobility.com
TermsOfService function · typescript · L25-L127 (103 LOC)app/terms/page.tsx
export default function TermsOfService() {
const sections = [
{
title: '1. Acceptance of Terms',
content: 'By accessing and using the COMEX Metals Supply & Demand Tracker ("the Service"), you agree to be bound by these Terms of Service. If you do not agree to these terms, please do not use this Service.',
},
{
title: '2. Description of Service',
content: 'This Service provides aggregated data and analysis of COMEX precious metals inventory, delivery notices, and market activity. Data is sourced from publicly available CME Group reports including DLV665-T delivery reports and depository statistics.',
},
{
title: '3. Data Disclaimer',
content: 'All information provided is for informational and educational purposes only. This Service does NOT constitute financial advice, investment recommendations, or trading signals. Data may be delayed, incomplete, or contain errors. Users should verify all information independently before makingImage function · typescript · L11-L142 (132 LOC)app/twitter-image.tsx
export default async function Image() {
return new ImageResponse(
(
<div
style={{
height: '100%',
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#0f172a',
backgroundImage: 'linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%)',
}}
>
{/* Top accent line */}
<div
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '4px',
background: 'linear-gradient(90deg, #fbbf24, #f59e0b, #d97706)',
}}
/>
{/* Main content */}
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
padding: '60px',
}}
>
{/* Logo/Icon */AuthProvider function · typescript · L5-L7 (3 LOC)components/AuthProvider.tsx
export default function AuthProvider({ children }: { children: React.ReactNode }) {
return <SessionProvider>{children}</SessionProvider>;
}CollapsibleReferences function · typescript · L6-L34 (29 LOC)components/CollapsibleReferences.tsx
export default function CollapsibleReferences({ children }: { children: React.ReactNode }) {
const [open, setOpen] = useState(false);
return (
<div className="border-t border-slate-200 dark:border-slate-700 pt-20 mb-10">
<button
onClick={() => setOpen(!open)}
className="flex items-center gap-2.5 mb-5 w-full group cursor-pointer"
>
<div className="w-8 h-8 rounded-lg flex items-center justify-center bg-slate-500">
<BookOpen className="w-4 h-4 text-white" />
</div>
<h2 className="text-lg sm:text-xl font-bold text-slate-900 dark:text-white">
References
</h2>
<ChevronDown
className={`w-5 h-5 text-slate-400 dark:text-slate-500 ml-auto transition-transform duration-300 ${open ? 'rotate-180' : ''}`}
/>
</button>
<div
className={`overflow-hidden transition-all duration-500 ease-in-out ${open ? 'max-h-[5000px] opacity-100' : 'max-h-0 opacity-0'}`}
>
getOpenInterestForMetal function · typescript · L220-L264 (45 LOC)components/Dashboard.tsx
function getOpenInterestForMetal(
futuresSymbol: string | undefined,
bulletinData: BulletinData | null | undefined,
volumeSummaryData: VolumeSummaryData | null | undefined
): number {
if (!futuresSymbol) return 0;
// Handle combined PL+PA for Platinum & Palladium
if (futuresSymbol === 'PL+PA') {
let totalOI = 0;
// Try volume summary first
if (volumeSummaryData?.products) {
const plProduct = volumeSummaryData.products.find(p => p.symbol === 'PL');
const paProduct = volumeSummaryData.products.find(p => p.symbol === 'PA');
if (plProduct) totalOI += plProduct.open_interest;
if (paProduct) totalOI += paProduct.open_interest;
if (totalOI > 0) return totalOI;
}
// Fallback to bulletin data
if (bulletinData?.products) {
const plProduct = bulletinData.products.find(p => p.symbol === 'PL');
const paProduct = bulletinData.products.find(p => p.symbol === 'PA');
if (plProduct) totalOI += plProduct.totaformatNumber function · typescript · L55-L57 (3 LOC)components/DeliveryMTDChart.tsx
function formatNumber(num: number): string {
return num.toLocaleString('en-US');
}formatShortDate function · typescript · L59-L66 (8 LOC)components/DeliveryMTDChart.tsx
function formatShortDate(dateStr: string): string {
const parts = dateStr.split('/');
if (parts.length !== 3) return dateStr;
const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const month = parseInt(parts[0]) - 1;
const day = parseInt(parts[1]);
return `${monthNames[month]} ${day}`;
}DeliveryMTDChart function · typescript · L70-L373 (304 LOC)components/DeliveryMTDChart.tsx
export default function DeliveryMTDChart({ data }: DeliveryMTDChartProps) {
const { resolvedTheme } = useTheme();
const isDark = resolvedTheme === 'dark';
const chartRef = useRef<HTMLDivElement>(null);
const mainContracts = data.contracts.filter(c =>
['Gold', 'Silver', 'Copper', 'Platinum', 'Palladium', 'Aluminum'].includes(c.metal)
);
const [selectedMetal, setSelectedMetal] = useState<string>(
mainContracts.find(c => c.metal === 'Gold')?.metal || mainContracts[0]?.metal || 'Gold'
);
const [viewMode, setViewMode] = useState<ViewMode>('daily');
const contract = mainContracts.find(c => c.metal === selectedMetal);
if (!contract) return null;
const color = metalColors[selectedMetal] || '#94a3b8';
const chartData = contract.daily_data.map(d => ({
date: formatShortDate(d.date),
daily: d.daily,
cumulative: d.cumulative,
}));
const activeDays = contract.daily_data.filter(d => d.daily > 0);
const activeDayCount = activeDays.length;
consRepobility (the analyzer behind this table) · https://repobility.com
formatNumber function · typescript · L36-L38 (3 LOC)components/DeliverySection.tsx
function formatNumber(num: number): string {
return num.toLocaleString('en-US');
}formatCurrency function · typescript · L40-L45 (6 LOC)components/DeliverySection.tsx
function formatCurrency(num: number): string {
if (num >= 1000) {
return `$${num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
}
return `$${num.toFixed(2)}`;
}DeliveryExplainer function · typescript · L47-L78 (32 LOC)components/DeliverySection.tsx
function DeliveryExplainer() {
const [open, setOpen] = useState(false);
return (
<div className="max-w-lg">
<button
onClick={() => setOpen(!open)}
className="inline-flex items-center gap-1.5 text-xs sm:text-sm font-semibold text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white transition-colors"
>
<HelpCircle className="w-3.5 h-3.5" />
<span>What are issues, stops & settlement?</span>
<ChevronDown className={`w-3.5 h-3.5 transition-transform duration-200 ${open ? 'rotate-180' : ''}`} />
</button>
<div
className={`grid transition-all duration-300 ease-in-out ${open ? 'grid-rows-[1fr] opacity-100 mt-3' : 'grid-rows-[0fr] opacity-0 mt-0'}`}
>
<div className="overflow-hidden">
<p className="text-xs sm:text-sm text-slate-400 dark:text-slate-500 leading-relaxed mb-3">
When a futures contract expires, holders can take physical delivery. An <strong classN