diff --git a/website/package-lock.json b/website/package-lock.json index 841e054..1413bf0 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -13,6 +13,7 @@ "@tailwindcss/postcss": "^4.1.7", "@tailwindcss/typography": "^0.5.16", "@visx/scale": "^3.12.0", + "apexcharts": "^4.7.0", "better-auth": "^1.2.8", "drizzle-orm": "^0.33.0", "ioredis": "^5.6.1", @@ -20,6 +21,7 @@ "lucide-svelte": "^0.511.0", "mode-watcher": "^1.0.7", "postgres": "^3.4.4", + "svelte-apexcharts": "^1.0.2", "svelte-lightweight-charts": "^2.2.0" }, "devDependencies": { @@ -1972,6 +1974,57 @@ "vite": "^5.0.0" } }, + "node_modules/@svgdotjs/svg.draggable.js": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@svgdotjs/svg.draggable.js/-/svg.draggable.js-3.0.6.tgz", + "integrity": "sha512-7iJFm9lL3C40HQcqzEfezK2l+dW2CpoVY3b77KQGqc8GXWa6LhhmX5Ckv7alQfUXBuZbjpICZ+Dvq1czlGx7gA==", + "peerDependencies": { + "@svgdotjs/svg.js": "^3.2.4" + } + }, + "node_modules/@svgdotjs/svg.filter.js": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@svgdotjs/svg.filter.js/-/svg.filter.js-3.0.9.tgz", + "integrity": "sha512-/69XMRCDoam2HgC4ldHIaDgeQf1ViHIsa0Ld4uWgiXtZ+E24DWHe/9Ib6kbNiZ7WRIdlVokUDR1Fg0kjIpkfbw==", + "dependencies": { + "@svgdotjs/svg.js": "^3.2.4" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@svgdotjs/svg.js": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@svgdotjs/svg.js/-/svg.js-3.2.4.tgz", + "integrity": "sha512-BjJ/7vWNowlX3Z8O4ywT58DqbNRyYlkk6Yz/D13aB7hGmfQTvGX4Tkgtm/ApYlu9M7lCQi15xUEidqMUmdMYwg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Fuzzyma" + } + }, + "node_modules/@svgdotjs/svg.resize.js": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@svgdotjs/svg.resize.js/-/svg.resize.js-2.0.5.tgz", + "integrity": "sha512-4heRW4B1QrJeENfi7326lUPYBCevj78FJs8kfeDxn5st0IYPIRXoTtOSYvTzFWgaWWXd3YCDE6ao4fmv91RthA==", + "engines": { + "node": ">= 14.18" + }, + "peerDependencies": { + "@svgdotjs/svg.js": "^3.2.4", + "@svgdotjs/svg.select.js": "^4.0.1" + } + }, + "node_modules/@svgdotjs/svg.select.js": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@svgdotjs/svg.select.js/-/svg.select.js-4.0.3.tgz", + "integrity": "sha512-qkMgso1sd2hXKd1FZ1weO7ANq12sNmQJeGDjs46QwDVsxSRcHmvWKL2NDF7Yimpwf3sl5esOLkPqtV2bQ3v/Jg==", + "engines": { + "node": ">= 14.18" + }, + "peerDependencies": { + "@svgdotjs/svg.js": "^3.2.4" + } + }, "node_modules/@swc/helpers": { "version": "0.5.17", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", @@ -2396,6 +2449,11 @@ "node": ">=12" } }, + "node_modules/@yr/monotone-cubic-spline": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz", + "integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==" + }, "node_modules/acorn": { "version": "8.14.1", "license": "MIT", @@ -2406,6 +2464,19 @@ "node": ">=0.4.0" } }, + "node_modules/apexcharts": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-4.7.0.tgz", + "integrity": "sha512-iZSrrBGvVlL+nt2B1NpqfDuBZ9jX61X9I2+XV0hlYXHtTwhwLTHDKGXjNXAgFBDLuvSYCB/rq2nPWVPRv2DrGA==", + "dependencies": { + "@svgdotjs/svg.draggable.js": "^3.0.4", + "@svgdotjs/svg.filter.js": "^3.0.8", + "@svgdotjs/svg.js": "^3.2.4", + "@svgdotjs/svg.resize.js": "^2.0.2", + "@svgdotjs/svg.select.js": "^4.0.1", + "@yr/monotone-cubic-spline": "^1.0.3" + } + }, "node_modules/aria-query": { "version": "5.3.2", "license": "Apache-2.0", @@ -3905,6 +3976,31 @@ "node": ">=18" } }, + "node_modules/svelte-apexcharts": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/svelte-apexcharts/-/svelte-apexcharts-1.0.2.tgz", + "integrity": "sha512-6qlx4rE+XsonZ0FZudfwqOQ34Pq+3wpxgAD75zgEmGoYhYBJcwmikTuTf3o8ZBsZue9U/pAwhNy3ed1Bkq1gmA==", + "dependencies": { + "apexcharts": "^3.19.2" + }, + "peerDependencies": { + "apexcharts": "^3.19.2" + } + }, + "node_modules/svelte-apexcharts/node_modules/apexcharts": { + "version": "3.54.1", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.54.1.tgz", + "integrity": "sha512-E4et0h/J1U3r3EwS/WlqJCQIbepKbp6wGUmaAwJOMjHUP4Ci0gxanLa7FR3okx6p9coi4st6J853/Cb1NP0vpA==", + "dependencies": { + "@yr/monotone-cubic-spline": "^1.0.3", + "svg.draggable.js": "^2.2.2", + "svg.easing.js": "^2.0.0", + "svg.filter.js": "^2.0.2", + "svg.pathmorphing.js": "^0.1.3", + "svg.resize.js": "^1.4.3", + "svg.select.js": "^3.0.1" + } + }, "node_modules/svelte-check": { "version": "4.2.1", "dev": true, @@ -3984,6 +4080,89 @@ "svelte": "^5.0.0" } }, + "node_modules/svg.draggable.js": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", + "integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==", + "dependencies": { + "svg.js": "^2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.easing.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz", + "integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==", + "dependencies": { + "svg.js": ">=2.3.x" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.filter.js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz", + "integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==", + "dependencies": { + "svg.js": "^2.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz", + "integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==" + }, + "node_modules/svg.pathmorphing.js": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz", + "integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==", + "dependencies": { + "svg.js": "^2.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.resize.js": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz", + "integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==", + "dependencies": { + "svg.js": "^2.6.5", + "svg.select.js": "^2.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.resize.js/node_modules/svg.select.js": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz", + "integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==", + "dependencies": { + "svg.js": "^2.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.select.js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz", + "integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==", + "dependencies": { + "svg.js": "^2.6.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/tabbable": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", diff --git a/website/package.json b/website/package.json index 38d180e..b2aa517 100644 --- a/website/package.json +++ b/website/package.json @@ -44,6 +44,7 @@ "@tailwindcss/postcss": "^4.1.7", "@tailwindcss/typography": "^0.5.16", "@visx/scale": "^3.12.0", + "apexcharts": "^4.7.0", "better-auth": "^1.2.8", "drizzle-orm": "^0.33.0", "ioredis": "^5.6.1", @@ -51,6 +52,7 @@ "lucide-svelte": "^0.511.0", "mode-watcher": "^1.0.7", "postgres": "^3.4.4", + "svelte-apexcharts": "^1.0.2", "svelte-lightweight-charts": "^2.2.0" } } diff --git a/website/src/lib/components/self/AppSidebar.svelte b/website/src/lib/components/self/AppSidebar.svelte index e18cd0a..6a25cb5 100644 --- a/website/src/lib/components/self/AppSidebar.svelte +++ b/website/src/lib/components/self/AppSidebar.svelte @@ -23,7 +23,8 @@ Settings, Gift, Shield, - Ticket + Ticket, + BarChart3 } from 'lucide-svelte'; import { mode, setMode } from 'mode-watcher'; import type { HTMLAttributes } from 'svelte/elements'; @@ -44,6 +45,7 @@ { title: 'Market', url: '/market', icon: Store }, { title: 'Portfolio', url: '/portfolio', icon: BriefcaseBusiness }, { title: 'Leaderboard', url: '/leaderboard', icon: Trophy }, + { title: 'Treemap', url: '/treemap', icon: BarChart3 }, { title: 'Create coin', url: '/coin/create', icon: Coins } ] }; diff --git a/website/src/lib/components/self/DailyRewards.svelte b/website/src/lib/components/self/DailyRewards.svelte index d88459e..04757f1 100644 --- a/website/src/lib/components/self/DailyRewards.svelte +++ b/website/src/lib/components/self/DailyRewards.svelte @@ -1,6 +1,6 @@ + +
+
+
+ +
+ +
+ + +
+
+
+
+ +
+ + +
+
+
+ + +
+ + + +
+
+ +
+ +
+ +
+ + +
+ + + + +
+ {#each Array(6) as _} + + {/each} +
+
+
+ + + +
+
+ + +
+ + + + + + + + + +
+ + +
+
+
+ + + + + + + + + +
+
+ +
+
+ + +
+
+ + +
+
+
+
+ +
+
+ + +
+
+ + +
+
+
+
+
+
+
+
+ + +
+ {#each Array(4) as _} + + + + + + + + + + + + + {/each} +
+ + + + + + + + + + +
+ {#each Array(3) as _} +
+ +
+
+ + +
+ + +
+
+ {/each} +
+
+
+
diff --git a/website/src/lib/components/self/skeletons/HomeSkeleton.svelte b/website/src/lib/components/self/skeletons/HomeSkeleton.svelte new file mode 100644 index 0000000..30d45c5 --- /dev/null +++ b/website/src/lib/components/self/skeletons/HomeSkeleton.svelte @@ -0,0 +1,62 @@ + + +
+ +
+ + +
+ + +
+ {#each Array(6) as _} + + + +
+ + +
+ +
+ + + +
+ +
+ + +
+
+
+ {/each} +
+ + +
+ + + +
+ {#each Array(10) as _} +
+ +
+ + +
+ + + + +
+ {/each} +
+
+
+
+
diff --git a/website/src/lib/components/self/skeletons/LeaderboardSkeleton.svelte b/website/src/lib/components/self/skeletons/LeaderboardSkeleton.svelte new file mode 100644 index 0000000..62030ab --- /dev/null +++ b/website/src/lib/components/self/skeletons/LeaderboardSkeleton.svelte @@ -0,0 +1,36 @@ + + +
+ {#each Array(4) as _} + + + + + + + + + + + +
+ {#each Array(5) as _} +
+ + +
+ + +
+ + +
+ {/each} +
+
+
+ {/each} +
diff --git a/website/src/lib/components/self/skeletons/MarketSkeleton.svelte b/website/src/lib/components/self/skeletons/MarketSkeleton.svelte new file mode 100644 index 0000000..5ba0187 --- /dev/null +++ b/website/src/lib/components/self/skeletons/MarketSkeleton.svelte @@ -0,0 +1,65 @@ + + + +
+ + +
+ + +
+ {#each Array(perPage) as _} + + +
+
+ +
+ + +
+
+
+ +
+
+
+ + +
+ +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ {/each} +
diff --git a/website/src/lib/components/self/skeletons/ProfileSkeleton.svelte b/website/src/lib/components/self/skeletons/ProfileSkeleton.svelte new file mode 100644 index 0000000..c69f23c --- /dev/null +++ b/website/src/lib/components/self/skeletons/ProfileSkeleton.svelte @@ -0,0 +1,128 @@ + + + + + +
+ +
+ +
+ + +
+
+
+ + +
+ +
+ + + + +
+ + +
+
+
+
+
+ + +
+ {#each Array(4) as _} + + +
+ + +
+ + +
+
+ {/each} +
+ + +
+ {#each Array(4) as _} + + +
+ + +
+
+ + +
+
+ + +
+
+
+ {/each} +
+ + + + + + + + + + + +
+ {#each Array(3) as _} +
+ +
+ + +
+ + + +
+ {/each} +
+
+
+ + + + + + + + + + + +
+ {#each Array(5) as _} +
+ + +
+ + +
+ + + +
+ {/each} +
+
+
diff --git a/website/src/lib/utils.ts b/website/src/lib/utils.ts index 5e4bd31..6774004 100644 --- a/website/src/lib/utils.ts +++ b/website/src/lib/utils.ts @@ -64,6 +64,16 @@ export function formatValue(value: number | string): string { if (numValue >= 1e9) return `$${(numValue / 1e9).toFixed(2)}B`; if (numValue >= 1e6) return `$${(numValue / 1e6).toFixed(2)}M`; if (numValue >= 1e3) return `$${(numValue / 1e3).toFixed(2)}K`; + + if (numValue < 0.01) { + const str = numValue.toString(); + const match = str.match(/^0\.0*(\d{2})/); + if (match) { + const zerosAfterDecimal = str.indexOf(match[1]) - 2; + return `$${numValue.toFixed(zerosAfterDecimal + 2)}`; + } + } + return `$${numValue.toFixed(2)}`; } diff --git a/website/src/routes/+page.svelte b/website/src/routes/+page.svelte index 817e994..6bcde8f 100644 --- a/website/src/routes/+page.svelte +++ b/website/src/routes/+page.svelte @@ -6,6 +6,7 @@ import SignInConfirmDialog from '$lib/components/self/SignInConfirmDialog.svelte'; import CoinIcon from '$lib/components/self/CoinIcon.svelte'; import DataTable from '$lib/components/self/DataTable.svelte'; + import HomeSkeleton from '$lib/components/self/skeletons/HomeSkeleton.svelte'; import { onMount } from 'svelte'; import { toast } from 'svelte-sonner'; import { goto } from '$app/navigation'; @@ -102,11 +103,7 @@ {#if loading} -
-
-
Loading market data...
-
-
+ {:else if coins.length === 0}
diff --git a/website/src/routes/admin/promo/+page.svelte b/website/src/routes/admin/promo/+page.svelte index d57e434..d9605b6 100644 --- a/website/src/routes/admin/promo/+page.svelte +++ b/website/src/routes/admin/promo/+page.svelte @@ -13,7 +13,7 @@ import { Alert, AlertDescription } from '$lib/components/ui/alert'; import { Badge } from '$lib/components/ui/badge'; import { Skeleton } from '$lib/components/ui/skeleton'; - import { Plus, Ticket, Users, Calendar, CheckCircle, XCircle, Loader2 } from 'lucide-svelte'; + import { Plus, Ticket, Users, Calendar, XCircle, Loader2, CheckIcon } from 'lucide-svelte'; import { USER_DATA } from '$lib/stores/user-data'; import { formatDate, getExpirationDate } from '$lib/utils'; import type { PromoCode } from '$lib/types/promo-code'; @@ -216,7 +216,7 @@ class={createSuccess ? 'text-success' : ''} > {#if createSuccess} - + {:else} {/if} diff --git a/website/src/routes/api/coin/[coinSymbol]/trade/+server.ts b/website/src/routes/api/coin/[coinSymbol]/trade/+server.ts index d38bfc3..c6b8d4c 100644 --- a/website/src/routes/api/coin/[coinSymbol]/trade/+server.ts +++ b/website/src/routes/api/coin/[coinSymbol]/trade/+server.ts @@ -180,6 +180,13 @@ export async function POST({ params, request }) { updatedAt: new Date() }) .where(eq(coin.id, coinData.id)); + + await redis.publish(`prices:${normalizedSymbol}`, JSON.stringify({ + currentPrice: newPrice, + marketCap: Number(coinData.circulatingSupply) * newPrice, + change24h: metrics.change24h, + volume24h: metrics.volume24h + })); }); // REDIS @@ -313,6 +320,13 @@ export async function POST({ params, request }) { updatedAt: new Date() }) .where(eq(coin.id, coinData.id)); + + await redis.publish(`prices:${normalizedSymbol}`, JSON.stringify({ + currentPrice: newPrice, + marketCap: Number(coinData.circulatingSupply) * newPrice, + change24h: metrics.change24h, + volume24h: metrics.volume24h + })); }); // REDIS diff --git a/website/src/routes/coin/[coinSymbol]/+page.svelte b/website/src/routes/coin/[coinSymbol]/+page.svelte index 87e6912..837de37 100644 --- a/website/src/routes/coin/[coinSymbol]/+page.svelte +++ b/website/src/routes/coin/[coinSymbol]/+page.svelte @@ -7,6 +7,7 @@ import TradeModal from '$lib/components/self/TradeModal.svelte'; import CommentSection from '$lib/components/self/CommentSection.svelte'; import UserProfilePreview from '$lib/components/self/UserProfilePreview.svelte'; + import CoinSkeleton from '$lib/components/self/skeletons/CoinSkeleton.svelte'; import { TrendingUp, TrendingDown, DollarSign, Coins, ChartColumn } from 'lucide-svelte'; import { createChart, @@ -259,26 +260,17 @@ onSuccess={handleTradeSuccess} /> {/if} - -{#if loading} -
-
-
-
Loading coin data...
-
-
-
-{:else if !coin} -
+
+ {#if loading} + + {:else if !coin}
Coin not found
-
-{:else} -
+ {:else}
@@ -535,5 +527,5 @@
-
-{/if} + {/if} +
diff --git a/website/src/routes/leaderboard/+page.svelte b/website/src/routes/leaderboard/+page.svelte index 921a6b6..0fe946e 100644 --- a/website/src/routes/leaderboard/+page.svelte +++ b/website/src/routes/leaderboard/+page.svelte @@ -4,6 +4,7 @@ import { Badge } from '$lib/components/ui/badge'; import { Button } from '$lib/components/ui/button'; import DataTable from '$lib/components/self/DataTable.svelte'; + import LeaderboardSkeleton from '$lib/components/self/skeletons/LeaderboardSkeleton.svelte'; import { onMount } from 'svelte'; import { toast } from 'svelte-sonner'; import { goto } from '$app/navigation'; @@ -215,11 +216,7 @@ {#if loading} -
-
-
Loading leaderboard...
-
-
+ {:else if !leaderboardData}
diff --git a/website/src/routes/market/+page.svelte b/website/src/routes/market/+page.svelte index dc979e9..65b9054 100644 --- a/website/src/routes/market/+page.svelte +++ b/website/src/routes/market/+page.svelte @@ -8,6 +8,7 @@ import { Badge } from '$lib/components/ui/badge'; import { Label } from '$lib/components/ui/label'; import CoinIcon from '$lib/components/self/CoinIcon.svelte'; + import MarketSkeleton from '$lib/components/self/skeletons/MarketSkeleton.svelte'; import { onMount } from 'svelte'; import { toast } from 'svelte-sonner'; import { goto } from '$app/navigation'; @@ -410,12 +411,7 @@ {#if loading} -
-
-
Loading market data...
-
Fetching the latest coin prices and chaos levels
-
-
+ {:else if coins.length === 0}
diff --git a/website/src/routes/treemap/+page.svelte b/website/src/routes/treemap/+page.svelte new file mode 100644 index 0000000..711f695 --- /dev/null +++ b/website/src/routes/treemap/+page.svelte @@ -0,0 +1,350 @@ + + + + Treemap - Rugplay + + + +
+
+
+
+
+ +

Market Treemap

+
+
+ + +
+
+

+ Visual representation of the cryptocurrency market. Size indicates market cap, color shows + 24h price change. +

+ {#if coins.length > 0} +

+ Last updated: {lastUpdated.toLocaleTimeString()} +

+ {/if} +
+ + {#if isLoading && coins.length === 0} + + + + + + + + + + + + + + + {:else if error} + + +
+ +

Failed to load treemap

+

{error}

+
+ +
+
+ {:else if coins.length === 0} + + +
+ +

No coins available

+

Create some coins to see the treemap visualization.

+
+
+
+ {:else} + + +
+
+
+ Positive 24h change +
+
+
+ Negative 24h change +
+ + {coins.length} coins + +
+
+
+
+ {/if} +
+
+ + diff --git a/website/src/routes/user/[username]/+page.svelte b/website/src/routes/user/[username]/+page.svelte index fc8a793..547d418 100644 --- a/website/src/routes/user/[username]/+page.svelte +++ b/website/src/routes/user/[username]/+page.svelte @@ -5,6 +5,7 @@ import { Button } from '$lib/components/ui/button'; import DataTable from '$lib/components/self/DataTable.svelte'; import ProfileBadges from '$lib/components/self/ProfileBadges.svelte'; + import ProfileSkeleton from '$lib/components/self/skeletons/ProfileSkeleton.svelte'; import { getPublicUrl, formatPrice, formatValue, formatQuantity, formatDate } from '$lib/utils'; import { onMount } from 'svelte'; import { toast } from 'svelte-sonner'; @@ -15,8 +16,7 @@ TrendingDown, Coins, Receipt, - Activity, - RefreshCw + Activity } from 'lucide-svelte'; import { goto } from '$app/navigation'; import type { UserProfileData } from '$lib/types/user-profile'; @@ -208,12 +208,7 @@
{#if loading} -
-
- -
Loading profile...
-
-
+ {:else if !profileData}