Token data

This commit is contained in:
2026-02-12 01:26:17 +01:00
parent 174f1ff0c1
commit 69681a81cc
5 changed files with 141 additions and 14 deletions
+1 -1
View File
@@ -69,7 +69,7 @@
--secondary-foreground: oklch(0.96 0 0); --secondary-foreground: oklch(0.96 0 0);
/* Muted / subtle text */ /* Muted / subtle text */
--muted: oklch(0.24 0.015 260); --muted: oklch(22.068% 0.01327 258.402);
--muted-foreground: oklch(0.72 0.01 260); --muted-foreground: oklch(0.72 0.01 260);
/* Accent – SOL mint */ /* Accent – SOL mint */
+1 -1
View File
@@ -39,7 +39,7 @@ export default function PairsCard({ title }: { title: string }) {
</Button> </Button>
</div> </div>
</CardHeader> </CardHeader>
<CardContent className="px-4 py-4 border-b border-border "> <CardContent className="px-4 py-4 border-b border-border hover:bg-muted">
<TokenCard <TokenCard
token={{ symbol: "SYM", name: "My token", mint: "myMint" }} token={{ symbol: "SYM", name: "My token", mint: "myMint" }}
/> />
+83 -12
View File
@@ -1,23 +1,94 @@
import { Token } from "@/modules/tokens/types"; import { Badge } from "@/components/ui/badge";
import { CopyIcon } from "lucide-react"; import { cn } from "@/lib/utils";
import { Token, TokenLiveData } from "@/modules/tokens/types";
import {
BoxesIcon,
ChefHatIcon,
CoinsIcon,
CopyIcon,
CrosshairIcon,
EyeIcon,
GhostIcon,
IconNode,
LucideIcon,
UsersIcon,
UserStarIcon,
} from "lucide-react";
export default function TokenCard({ token }: { token: Token }) { export default function TokenCard({ token }: { token: Token }) {
const live: TokenLiveData = {
topHolder: 23,
devHold: 10,
snipersHold: 4,
insidersHold: 2,
bundleHold: 3,
};
return ( return (
<div className="flex gap-4"> <div className="flex gap-4">
<div className="h-full overflow-hidden min-w-0"> <div className="h-full overflow-hidden min-w-0">
<img {token.logoURI ? (
src={token.logoURI} <img
alt="Token" src={token.logoURI}
className="w-16 h-16 bg-amber-50 rounded-lg" alt="Token"
/> className="w-16 h-16 rounded-lg"
/>
) : (
<div className="w-16 h-16 bg-primary/20 rounded-lg flex items-center justify-center">
<CoinsIcon className="text-primary" />
</div>
)}
<p className="mt-1 truncate text-xs text-muted-foreground"></p> <p className="mt-1 truncate text-xs text-muted-foreground"></p>
</div> </div>
<div className="flex gap-1.5"> <div className="flex flex-col">
<p className="font-semibold">{token.symbol}</p> <div className="flex gap-1.5">
<p className="text-muted-foreground flex gap-1 items-center h-max cursor-pointer hover:text-accent"> <p className="font-semibold">{token.symbol}</p>
{token.name} <CopyIcon className="w-4 h-4" /> <p className="text-muted-foreground flex gap-1 items-center h-max cursor-pointer hover:text-accent">
</p> {token.name} <CopyIcon className="w-4 h-4" />
</p>
</div>
<div className="items-center flex gap-3">
<p className="text-green-500 text-sm">20m</p>
<p className="text-sm flex gap-1 items-center">
<UsersIcon className="w-3.5 h-3.5 text-muted-foreground" />
10
</p>
<p className="text-sm flex gap-1 items-center">
<EyeIcon className="w-3.5 h-3.5" />
10
</p>
</div>
<div className="items-center flex gap-2">
<TokenBadge value={live.topHolder} icon={UserStarIcon} />
<TokenBadge value={live.devHold} icon={ChefHatIcon} />
<TokenBadge value={live.snipersHold} icon={CrosshairIcon} />
<TokenBadge value={live.insidersHold} icon={GhostIcon} />
<TokenBadge value={live.bundleHold} icon={BoxesIcon} />
</div>
</div> </div>
</div> </div>
); );
} }
function TokenBadge({
value,
icon: Icon,
redWhen = 10,
}: {
value: number;
icon: LucideIcon;
redWhen?: number;
}) {
return (
<Badge
variant="ghost"
className={cn(
"bg-card",
value > redWhen ? "text-red-400" : "text-green-500",
)}
>
<Icon />
{value}%
</Badge>
);
}
+48
View File
@@ -0,0 +1,48 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"
import { cn } from "@/lib/utils"
const badgeVariants = cva(
"inline-flex items-center justify-center rounded-full border border-transparent px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
secondary:
"bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
destructive:
"bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
link: "text-primary underline-offset-4 [a&]:hover:underline",
},
},
defaultVariants: {
variant: "default",
},
}
)
function Badge({
className,
variant = "default",
asChild = false,
...props
}: React.ComponentProps<"span"> &
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
const Comp = asChild ? Slot.Root : "span"
return (
<Comp
data-slot="badge"
data-variant={variant}
className={cn(badgeVariants({ variant }), className)}
{...props}
/>
)
}
export { Badge, badgeVariants }
+8
View File
@@ -4,3 +4,11 @@ export interface Token {
name: string; name: string;
logoURI?: string; logoURI?: string;
} }
export interface TokenLiveData {
topHolder: number;
devHold: number;
snipersHold: number;
insidersHold: number;
bundleHold: number;
}