Spaces:
Build error
Build error
<script lang="ts"> | |
import { onMount } from 'svelte'; | |
let loading = true; | |
let error: string | null = null; | |
let stats = { | |
revenue: 0, | |
users: 0, | |
orders: 0, | |
conversion: 0 | |
}; | |
onMount(async () => { | |
try { | |
await new Promise(resolve => setTimeout(resolve, 1000)); | |
stats = { | |
revenue: 124500, | |
users: 2341, | |
orders: 342, | |
conversion: 3.2 | |
}; | |
loading = false; | |
} catch (err) { | |
error = 'Failed to load dashboard data'; | |
loading = false; | |
} | |
}); | |
const formatCurrency = (amount: number) => | |
new Intl.NumberFormat('en-US', { | |
style: 'currency', | |
currency: 'USD', | |
minimumFractionDigits: 0, | |
maximumFractionDigits: 0 | |
}).format(amount); | |
const formatNumber = (num: number) => | |
new Intl.NumberFormat('en-US').format(num); | |
</script> | |
<svelte:head> | |
<style> | |
:root { | |
--primary: #3b82f6; | |
--success: #10b981; | |
--danger: #ef4444; | |
--gray-50: #f9fafb; | |
--gray-100: #f3f4f6; | |
--gray-200: #e5e7eb; | |
--gray-300: #d1d5db; | |
--gray-400: #9ca3af; | |
--gray-500: #6b7280; | |
--gray-600: #4b5563; | |
--gray-700: #374151; | |
--gray-800: #1f2937; | |
--gray-900: #111827; | |
} | |
* { box-sizing: border-box; } | |
body { | |
margin: 0; | |
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; | |
background-color: var(--gray-50); | |
color: var(--gray-900); | |
line-height: 1.5; | |
} | |
.container { | |
max-width: 1200px; | |
margin: 0 auto; | |
padding: 2rem 1rem; | |
} | |
header { margin-bottom: 2rem; } | |
h1 { | |
margin: 0; | |
font-size: 2rem; | |
font-weight: 700; | |
color: var(--gray-900); | |
} | |
.subtitle { | |
margin: 0.25rem 0 0; | |
color: var(--gray-600); | |
} | |
.grid { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); | |
gap: 1.5rem; | |
margin-bottom: 2rem; | |
} | |
.loading, .error { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
min-height: 400px; | |
color: var(--gray-500); | |
} | |
.spinner { | |
width: 40px; | |
height: 40px; | |
border: 4px solid var(--gray-200); | |
border-top: 4px solid var(--primary); | |
border-radius: 50%; | |
animation: spin 1s linear infinite; | |
} | |
@keyframes spin { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
.error { color: var(--danger); } | |
.card { | |
background: white; | |
border-radius: 0.5rem; | |
padding: 1.5rem; | |
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); | |
transition: box-shadow 0.15s; | |
} | |
.card:hover { | |
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); | |
} | |
.card-title { | |
margin: 0 0 0.5rem; | |
font-size: 0.875rem; | |
font-weight: 500; | |
color: var(--gray-600); | |
text-transform: uppercase; | |
letter-spacing: 0.05em; | |
} | |
.card-value { | |
margin: 0; | |
font-size: 1.875rem; | |
font-weight: 700; | |
color: var(--gray-900); | |
} | |
.card-change { | |
margin-top: 0.5rem; | |
font-size: 0.875rem; | |
font-weight: 500; | |
} | |
.positive { color: var(--success); } | |
.negative { color: var(--danger); } | |
.chart-section { | |
background: white; | |
border-radius: 0.5rem; | |
padding: 1.5rem; | |
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); | |
} | |
.chart-section h2 { | |
margin: 0 0 1rem; | |
font-size: 1.25rem; | |
font-weight: 600; | |
color: var(--gray-900); | |
} | |
.chart-placeholder { | |
width: 100%; | |
height: 200px; | |
background: var(--gray-100); | |
border: 2px dashed var(--gray-300); | |
border-radius: 0.5rem; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
color: var(--gray-500); | |
font-size: 1rem; | |
} | |
@media (max-width: 640px) { | |
.container { padding: 1rem; } | |
h1 { font-size: 1.5rem; } | |
.grid { grid-template-columns: 1fr; } | |
} | |
</style> | |
</svelte:head> | |
<main class="container"> | |
<header> | |
<h1>Dashboard</h1> | |
<p class="subtitle">Overview of your business performance</p> | |
</header> | |
{#if loading} | |
<div class="loading"> | |
<div class="spinner"></div> | |
<p>Loading dashboard...</p> | |
</div> | |
{:else if error} | |
<div class="error"> | |
<p>{error}</p> | |
</div> | |
{:else} | |
<section class="grid"> | |
<div class="card"> | |
<h3 class="card-title">Revenue</h3> | |
<p class="card-value">{formatCurrency(stats.revenue)}</p> | |
<p class="card-change positive">+12.5%</p> | |
</div> | |
<div class="card"> | |
<h3 class="card-title">Active Users</h3> | |
<p class="card-value">{formatNumber(stats.users)}</p> | |
<p class="card-change positive">+5.2%</p> | |
</div> | |
<div class="card"> | |
<h3 class="card-title">Orders</h3> | |
<p class="card-value">{formatNumber(stats.orders)}</p> | |
<p class="card-change negative">-2.4%</p> | |
</div> | |
<div class="card"> | |
<h3 class="card-title">Conversion</h3> | |
<p class="card-value">{stats.conversion}%</p> | |
<p class="card-change positive">+0.3%</p> | |
</div> | |
</section> | |
<section class="chart-section"> | |
<h2>Revenue Trend</h2> | |
<div class="chart-placeholder">Chart placeholder</div> | |
</section> | |
{/if} | |
</main> |