@medialane/ui 0.312.0 → 0.314.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/account-page.cjs +48 -46
- package/dist/components/account-page.cjs.map +1 -1
- package/dist/components/account-page.js +48 -46
- package/dist/components/account-page.js.map +1 -1
- package/dist/components/approve-mint-dialog.cjs +1 -1
- package/dist/components/approve-mint-dialog.cjs.map +1 -1
- package/dist/components/approve-mint-dialog.js +1 -1
- package/dist/components/approve-mint-dialog.js.map +1 -1
- package/dist/components/asset-marketplace-panel.cjs +1 -1
- package/dist/components/asset-marketplace-panel.cjs.map +1 -1
- package/dist/components/asset-marketplace-panel.js +1 -1
- package/dist/components/asset-marketplace-panel.js.map +1 -1
- package/dist/components/fast-mint/success-view.cjs +1 -1
- package/dist/components/fast-mint/success-view.cjs.map +1 -1
- package/dist/components/fast-mint/success-view.js +1 -1
- package/dist/components/fast-mint/success-view.js.map +1 -1
- package/dist/components/home-page.cjs +1 -1
- package/dist/components/home-page.cjs.map +1 -1
- package/dist/components/home-page.js +1 -1
- package/dist/components/home-page.js.map +1 -1
- package/dist/components/launchpad/tier-mint-presets.cjs +3 -3
- package/dist/components/launchpad/tier-mint-presets.cjs.map +1 -1
- package/dist/components/launchpad/tier-mint-presets.js +3 -3
- package/dist/components/launchpad/tier-mint-presets.js.map +1 -1
- package/dist/components/listings-grid.cjs +1 -1
- package/dist/components/listings-grid.cjs.map +1 -1
- package/dist/components/listings-grid.js +1 -1
- package/dist/components/listings-grid.js.map +1 -1
- package/dist/components/portfolio-header.cjs +4 -1
- package/dist/components/portfolio-header.cjs.map +1 -1
- package/dist/components/portfolio-header.d.cts +4 -1
- package/dist/components/portfolio-header.d.ts +4 -1
- package/dist/components/portfolio-header.js +4 -1
- package/dist/components/portfolio-header.js.map +1 -1
- package/dist/components/portfolio-orders.cjs +1 -1
- package/dist/components/portfolio-orders.cjs.map +1 -1
- package/dist/components/portfolio-orders.js +1 -1
- package/dist/components/portfolio-orders.js.map +1 -1
- package/dist/components/rewards/rewards-dashboard.cjs +2 -2
- package/dist/components/rewards/rewards-dashboard.cjs.map +1 -1
- package/dist/components/rewards/rewards-dashboard.js +2 -2
- package/dist/components/rewards/rewards-dashboard.js.map +1 -1
- package/dist/components/route-error.cjs +2 -6
- package/dist/components/route-error.cjs.map +1 -1
- package/dist/components/route-error.d.cts +1 -2
- package/dist/components/route-error.d.ts +1 -2
- package/dist/components/route-error.js +2 -6
- package/dist/components/route-error.js.map +1 -1
- package/dist/components/token-card.cjs +2 -2
- package/dist/components/token-card.cjs.map +1 -1
- package/dist/components/token-card.js +2 -2
- package/dist/components/token-card.js.map +1 -1
- package/package.json +1 -1
|
@@ -89,7 +89,7 @@ function ListingsGrid({ sort = "recent", currency, orderType = "", minPrice, max
|
|
|
89
89
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-24 gap-4 text-center", children: [
|
|
90
90
|
/* @__PURE__ */ jsx("p", { className: "text-2xl font-bold", children: empty.title }),
|
|
91
91
|
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground max-w-sm", children: empty.description }),
|
|
92
|
-
orderType !== "offers" && /* @__PURE__ */ jsx(Button, { variant: "outline", asChild: true, children: /* @__PURE__ */ jsx(Link, { href: "/create", children: "Create &
|
|
92
|
+
orderType !== "offers" && /* @__PURE__ */ jsx(Button, { variant: "outline", asChild: true, children: /* @__PURE__ */ jsx(Link, { href: "/create", children: "Create & Sell" }) })
|
|
93
93
|
] });
|
|
94
94
|
}
|
|
95
95
|
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/listings-grid.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useRef } from \"react\";\nimport { normalizeAddress, type ApiOrder, type SortOrder } from \"@medialane/sdk\";\nimport Link from \"./link.js\";\nimport { Button } from \"./button.js\";\nimport { ListingCard, ListingCardSkeleton } from \"./listing-card.js\";\nimport { ListingCardMenu } from \"./listing-card-menu.js\";\nimport { LoadMoreSentinel } from \"./load-more-sentinel.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport { useOrders } from \"../utils/use-orders.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\nimport { useUsdPrices, usdValueFor } from \"../utils/use-usd-prices.js\";\nimport { emptyMessages } from \"../utils/empty-messages.js\";\n\nconst PAGE_SIZE = 15;\nconst BACKEND_PAGE_SIZE = 50;\n\nexport interface ListingsGridProps {\n sort?: string;\n currency?: string;\n orderType?: string;\n minPrice?: string;\n maxPrice?: string;\n\n initialOrders?: ApiOrder[];\n onBuy: (order: ApiOrder) => void;\n}\n\nfunction MarketplaceListingCard({ order, onBuy }: { order: ApiOrder; onBuy?: (order: ApiOrder) => void }) {\n const resolveImage = useImageUrl();\n const usdPrices = useUsdPrices();\n return (\n <ListingCard\n order={order}\n onBuy={onBuy}\n overflowMenu={<ListingCardMenu order={order} />}\n imageUrl={resolveImage(order.token?.image)}\n usdValue={usdValueFor(order.price?.formatted, order.price?.currency, usdPrices)}\n />\n );\n}\n\nexport function ListingsGrid({ sort = \"recent\", currency, orderType = \"\", minPrice, maxPrice, initialOrders, onBuy }: ListingsGridProps) {\n const { walletAddress } = useMedialaneApp();\n const [backendPage, setBackendPage] = useState(1);\n const [visiblePages, setVisiblePages] = useState(1);\n const [allOrders, setAllOrders] = useState<ApiOrder[]>(initialOrders ?? []);\n\n const prevFilters = useRef({ sort, currency, orderType, minPrice, maxPrice });\n useEffect(() => {\n const f = prevFilters.current;\n if (f.sort !== sort || f.currency !== currency || f.orderType !== orderType || f.minPrice !== minPrice || f.maxPrice !== maxPrice) {\n prevFilters.current = { sort, currency, orderType, minPrice, maxPrice };\n setBackendPage(1);\n setVisiblePages(1);\n setAllOrders([]);\n }\n }, [sort, currency, orderType, minPrice, maxPrice]);\n\n const { orders, meta, isLoading } = useOrders({\n status: \"ACTIVE\",\n sort: sort as SortOrder,\n ...(currency ? { currency } : {}),\n ...(minPrice ? { minPrice } : {}),\n ...(maxPrice ? { maxPrice } : {}),\n page: backendPage,\n limit: BACKEND_PAGE_SIZE,\n });\n\n useEffect(() => {\n if (isLoading) return;\n if (backendPage === 1) {\n setAllOrders(orders);\n } else {\n setAllOrders((prev) => {\n const existing = new Set(prev.map((o) => o.orderHash));\n const newItems = orders.filter((o) => !existing.has(o.orderHash));\n return newItems.length > 0 ? [...prev, ...newItems] : prev;\n });\n }\n }, [orders, isLoading, backendPage]);\n\n const filteredOrders = orderType === \"offers\"\n ? allOrders.filter((o) => o.offer.itemType === \"ERC20\")\n : allOrders.filter((o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\");\n\n const visibleLimit = visiblePages * PAGE_SIZE;\n const displayedOrders = filteredOrders.slice(0, visibleLimit);\n\n const isInitialLoading = isLoading && allOrders.length === 0;\n const isLoadingMore = isLoading && allOrders.length > 0;\n const backendTotal = meta?.total ?? 0;\n const hasBackendMore = backendTotal > 0\n ? allOrders.length < backendTotal\n : orders.length === BACKEND_PAGE_SIZE;\n const hasBufferedOrders = filteredOrders.length > displayedOrders.length;\n const hasMore = hasBufferedOrders || hasBackendMore || isLoadingMore;\n\n useEffect(() => {\n if (isLoading || !hasBackendMore || filteredOrders.length >= visibleLimit) return;\n setBackendPage((p) => p + 1);\n }, [filteredOrders.length, hasBackendMore, isLoading, visibleLimit]);\n\n const isOwner = (order: ApiOrder) =>\n !!walletAddress && !!order.offerer && normalizeAddress(\"STARKNET\", order.offerer) === normalizeAddress(\"STARKNET\", walletAddress);\n\n const handleLoadMore = () => {\n setVisiblePages((p) => p + 1);\n };\n\n if (isInitialLoading || (filteredOrders.length === 0 && hasBackendMore)) {\n return (\n <div className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4\">\n {Array.from({ length: PAGE_SIZE }).map((_, i) => (\n <ListingCardSkeleton key={i} />\n ))}\n </div>\n );\n }\n\n if (filteredOrders.length === 0 && !isLoading && !hasBackendMore) {\n const empty = orderType === \"offers\" ? emptyMessages.offers : emptyMessages.listings;\n return (\n <div className=\"flex flex-col items-center justify-center py-24 gap-4 text-center\">\n <p className=\"text-2xl font-bold\">{empty.title}</p>\n <p className=\"text-muted-foreground max-w-sm\">{empty.description}</p>\n {orderType !== \"offers\" && (\n <Button variant=\"outline\" asChild>\n <Link href=\"/create\">Create &
|
|
1
|
+
{"version":3,"sources":["../../src/components/listings-grid.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useRef } from \"react\";\nimport { normalizeAddress, type ApiOrder, type SortOrder } from \"@medialane/sdk\";\nimport Link from \"./link.js\";\nimport { Button } from \"./button.js\";\nimport { ListingCard, ListingCardSkeleton } from \"./listing-card.js\";\nimport { ListingCardMenu } from \"./listing-card-menu.js\";\nimport { LoadMoreSentinel } from \"./load-more-sentinel.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport { useOrders } from \"../utils/use-orders.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\nimport { useUsdPrices, usdValueFor } from \"../utils/use-usd-prices.js\";\nimport { emptyMessages } from \"../utils/empty-messages.js\";\n\nconst PAGE_SIZE = 15;\nconst BACKEND_PAGE_SIZE = 50;\n\nexport interface ListingsGridProps {\n sort?: string;\n currency?: string;\n orderType?: string;\n minPrice?: string;\n maxPrice?: string;\n\n initialOrders?: ApiOrder[];\n onBuy: (order: ApiOrder) => void;\n}\n\nfunction MarketplaceListingCard({ order, onBuy }: { order: ApiOrder; onBuy?: (order: ApiOrder) => void }) {\n const resolveImage = useImageUrl();\n const usdPrices = useUsdPrices();\n return (\n <ListingCard\n order={order}\n onBuy={onBuy}\n overflowMenu={<ListingCardMenu order={order} />}\n imageUrl={resolveImage(order.token?.image)}\n usdValue={usdValueFor(order.price?.formatted, order.price?.currency, usdPrices)}\n />\n );\n}\n\nexport function ListingsGrid({ sort = \"recent\", currency, orderType = \"\", minPrice, maxPrice, initialOrders, onBuy }: ListingsGridProps) {\n const { walletAddress } = useMedialaneApp();\n const [backendPage, setBackendPage] = useState(1);\n const [visiblePages, setVisiblePages] = useState(1);\n const [allOrders, setAllOrders] = useState<ApiOrder[]>(initialOrders ?? []);\n\n const prevFilters = useRef({ sort, currency, orderType, minPrice, maxPrice });\n useEffect(() => {\n const f = prevFilters.current;\n if (f.sort !== sort || f.currency !== currency || f.orderType !== orderType || f.minPrice !== minPrice || f.maxPrice !== maxPrice) {\n prevFilters.current = { sort, currency, orderType, minPrice, maxPrice };\n setBackendPage(1);\n setVisiblePages(1);\n setAllOrders([]);\n }\n }, [sort, currency, orderType, minPrice, maxPrice]);\n\n const { orders, meta, isLoading } = useOrders({\n status: \"ACTIVE\",\n sort: sort as SortOrder,\n ...(currency ? { currency } : {}),\n ...(minPrice ? { minPrice } : {}),\n ...(maxPrice ? { maxPrice } : {}),\n page: backendPage,\n limit: BACKEND_PAGE_SIZE,\n });\n\n useEffect(() => {\n if (isLoading) return;\n if (backendPage === 1) {\n setAllOrders(orders);\n } else {\n setAllOrders((prev) => {\n const existing = new Set(prev.map((o) => o.orderHash));\n const newItems = orders.filter((o) => !existing.has(o.orderHash));\n return newItems.length > 0 ? [...prev, ...newItems] : prev;\n });\n }\n }, [orders, isLoading, backendPage]);\n\n const filteredOrders = orderType === \"offers\"\n ? allOrders.filter((o) => o.offer.itemType === \"ERC20\")\n : allOrders.filter((o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\");\n\n const visibleLimit = visiblePages * PAGE_SIZE;\n const displayedOrders = filteredOrders.slice(0, visibleLimit);\n\n const isInitialLoading = isLoading && allOrders.length === 0;\n const isLoadingMore = isLoading && allOrders.length > 0;\n const backendTotal = meta?.total ?? 0;\n const hasBackendMore = backendTotal > 0\n ? allOrders.length < backendTotal\n : orders.length === BACKEND_PAGE_SIZE;\n const hasBufferedOrders = filteredOrders.length > displayedOrders.length;\n const hasMore = hasBufferedOrders || hasBackendMore || isLoadingMore;\n\n useEffect(() => {\n if (isLoading || !hasBackendMore || filteredOrders.length >= visibleLimit) return;\n setBackendPage((p) => p + 1);\n }, [filteredOrders.length, hasBackendMore, isLoading, visibleLimit]);\n\n const isOwner = (order: ApiOrder) =>\n !!walletAddress && !!order.offerer && normalizeAddress(\"STARKNET\", order.offerer) === normalizeAddress(\"STARKNET\", walletAddress);\n\n const handleLoadMore = () => {\n setVisiblePages((p) => p + 1);\n };\n\n if (isInitialLoading || (filteredOrders.length === 0 && hasBackendMore)) {\n return (\n <div className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4\">\n {Array.from({ length: PAGE_SIZE }).map((_, i) => (\n <ListingCardSkeleton key={i} />\n ))}\n </div>\n );\n }\n\n if (filteredOrders.length === 0 && !isLoading && !hasBackendMore) {\n const empty = orderType === \"offers\" ? emptyMessages.offers : emptyMessages.listings;\n return (\n <div className=\"flex flex-col items-center justify-center py-24 gap-4 text-center\">\n <p className=\"text-2xl font-bold\">{empty.title}</p>\n <p className=\"text-muted-foreground max-w-sm\">{empty.description}</p>\n {orderType !== \"offers\" && (\n <Button variant=\"outline\" asChild>\n <Link href=\"/create\">Create & Sell</Link>\n </Button>\n )}\n </div>\n );\n }\n\n return (\n <>\n <div className=\"space-y-6\">\n <div className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4\">\n {displayedOrders.map((order) => (\n <MarketplaceListingCard key={order.orderHash} order={order} onBuy={isOwner(order) ? undefined : onBuy} />\n ))}\n {isLoadingMore &&\n Array.from({ length: PAGE_SIZE }).map((_, i) => (\n <ListingCardSkeleton key={`loading-${i}`} />\n ))}\n </div>\n\n <LoadMoreSentinel hasMore={hasMore} isLoading={isLoadingMore} onLoadMore={handleLoadMore} />\n\n {!hasMore && filteredOrders.length > PAGE_SIZE && (\n <p className=\"text-center text-xs text-muted-foreground\">\n All {filteredOrders.length} {orderType === \"offers\" ? \"offers\" : \"listings\"} shown\n </p>\n )}\n </div>\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -39,6 +39,8 @@ var import_address_display = require("./address-display.js");
|
|
|
39
39
|
function PortfolioHeader({
|
|
40
40
|
address,
|
|
41
41
|
score,
|
|
42
|
+
label = "Portfolio",
|
|
43
|
+
trailing,
|
|
42
44
|
className
|
|
43
45
|
}) {
|
|
44
46
|
const scoreChip = score && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rounded-full bg-gradient-to-r from-brand-orange to-brand-maeve px-3.5 py-1.5 flex items-baseline gap-2 whitespace-nowrap text-white", children: [
|
|
@@ -50,7 +52,7 @@ function PortfolioHeader({
|
|
|
50
52
|
] });
|
|
51
53
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: (0, import_cn.cn)("flex items-center justify-between gap-4", className), children: [
|
|
52
54
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "min-w-0", children: [
|
|
53
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs font-medium text-muted-foreground", children:
|
|
55
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs font-medium text-muted-foreground", children: label }),
|
|
54
56
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
55
57
|
import_address_display.AddressDisplay,
|
|
56
58
|
{
|
|
@@ -60,6 +62,7 @@ function PortfolioHeader({
|
|
|
60
62
|
}
|
|
61
63
|
)
|
|
62
64
|
] }),
|
|
65
|
+
trailing,
|
|
63
66
|
score?.href ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_link.default, { href: score.href, className: "shrink-0 active:opacity-80", children: scoreChip }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "shrink-0", children: scoreChip })
|
|
64
67
|
] });
|
|
65
68
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/portfolio-header.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { AddressDisplay } from \"./address-display.js\";\n\nexport interface PortfolioHeaderScore {\n\n levelName: string;\n totalXp: number;\n\n href?: string;\n}\n\nexport interface PortfolioHeaderProps {\n address: string;\n score?: PortfolioHeaderScore | null;\n className?: string;\n}\n\nexport function PortfolioHeader({\n address,\n score,\n className,\n}: PortfolioHeaderProps) {\n const scoreChip = score && (\n <div className=\"rounded-full bg-gradient-to-r from-brand-orange to-brand-maeve px-3.5 py-1.5 flex items-baseline gap-2 whitespace-nowrap text-white\">\n <span className=\"text-sm font-bold\">\n {score.levelName}\n </span>\n <span className=\"text-xs font-medium text-white/80 tabular-nums\">\n {score.totalXp.toLocaleString()} XP\n </span>\n </div>\n );\n\n return (\n <div className={cn(\"flex items-center justify-between gap-4\", className)}>\n <div className=\"min-w-0\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n
|
|
1
|
+
{"version":3,"sources":["../../src/components/portfolio-header.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport Link from \"./link.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { AddressDisplay } from \"./address-display.js\";\n\nexport interface PortfolioHeaderScore {\n\n levelName: string;\n totalXp: number;\n\n href?: string;\n}\n\nexport interface PortfolioHeaderProps {\n address: string;\n score?: PortfolioHeaderScore | null;\n label?: string;\n trailing?: ReactNode;\n className?: string;\n}\n\nexport function PortfolioHeader({\n address,\n score,\n label = \"Portfolio\",\n trailing,\n className,\n}: PortfolioHeaderProps) {\n const scoreChip = score && (\n <div className=\"rounded-full bg-gradient-to-r from-brand-orange to-brand-maeve px-3.5 py-1.5 flex items-baseline gap-2 whitespace-nowrap text-white\">\n <span className=\"text-sm font-bold\">\n {score.levelName}\n </span>\n <span className=\"text-xs font-medium text-white/80 tabular-nums\">\n {score.totalXp.toLocaleString()} XP\n </span>\n </div>\n );\n\n return (\n <div className={cn(\"flex items-center justify-between gap-4\", className)}>\n <div className=\"min-w-0\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n {label}\n </p>\n <AddressDisplay\n address={address}\n chars={6}\n className=\"text-2xl sm:text-3xl font-black tracking-tight text-foreground\"\n />\n </div>\n {trailing}\n {score?.href ? (\n <Link href={score.href} className=\"shrink-0 active:opacity-80\">\n {scoreChip}\n </Link>\n ) : (\n <div className=\"shrink-0\">{scoreChip}</div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAgCM;AA7BN,kBAAiB;AACjB,gBAAmB;AACnB,6BAA+B;AAkBxB,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,YAAY,SAChB,6CAAC,SAAI,WAAU,uIACb;AAAA,gDAAC,UAAK,WAAU,qBACb,gBAAM,WACT;AAAA,IACA,6CAAC,UAAK,WAAU,kDACb;AAAA,YAAM,QAAQ,eAAe;AAAA,MAAE;AAAA,OAClC;AAAA,KACF;AAGF,SACE,6CAAC,SAAI,eAAW,cAAG,2CAA2C,SAAS,GACrE;AAAA,iDAAC,SAAI,WAAU,WACb;AAAA,kDAAC,OAAE,WAAU,6CACV,iBACH;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,OAAO;AAAA,UACP,WAAU;AAAA;AAAA,MACZ;AAAA,OACF;AAAA,IACC;AAAA,IACA,OAAO,OACN,4CAAC,YAAAA,SAAA,EAAK,MAAM,MAAM,MAAM,WAAU,8BAC/B,qBACH,IAEA,4CAAC,SAAI,WAAU,YAAY,qBAAU;AAAA,KAEzC;AAEJ;","names":["Link"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
2
3
|
|
|
3
4
|
interface PortfolioHeaderScore {
|
|
4
5
|
levelName: string;
|
|
@@ -8,8 +9,10 @@ interface PortfolioHeaderScore {
|
|
|
8
9
|
interface PortfolioHeaderProps {
|
|
9
10
|
address: string;
|
|
10
11
|
score?: PortfolioHeaderScore | null;
|
|
12
|
+
label?: string;
|
|
13
|
+
trailing?: ReactNode;
|
|
11
14
|
className?: string;
|
|
12
15
|
}
|
|
13
|
-
declare function PortfolioHeader({ address, score, className, }: PortfolioHeaderProps): react_jsx_runtime.JSX.Element;
|
|
16
|
+
declare function PortfolioHeader({ address, score, label, trailing, className, }: PortfolioHeaderProps): react_jsx_runtime.JSX.Element;
|
|
14
17
|
|
|
15
18
|
export { PortfolioHeader, type PortfolioHeaderProps, type PortfolioHeaderScore };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
2
3
|
|
|
3
4
|
interface PortfolioHeaderScore {
|
|
4
5
|
levelName: string;
|
|
@@ -8,8 +9,10 @@ interface PortfolioHeaderScore {
|
|
|
8
9
|
interface PortfolioHeaderProps {
|
|
9
10
|
address: string;
|
|
10
11
|
score?: PortfolioHeaderScore | null;
|
|
12
|
+
label?: string;
|
|
13
|
+
trailing?: ReactNode;
|
|
11
14
|
className?: string;
|
|
12
15
|
}
|
|
13
|
-
declare function PortfolioHeader({ address, score, className, }: PortfolioHeaderProps): react_jsx_runtime.JSX.Element;
|
|
16
|
+
declare function PortfolioHeader({ address, score, label, trailing, className, }: PortfolioHeaderProps): react_jsx_runtime.JSX.Element;
|
|
14
17
|
|
|
15
18
|
export { PortfolioHeader, type PortfolioHeaderProps, type PortfolioHeaderScore };
|
|
@@ -6,6 +6,8 @@ import { AddressDisplay } from "./address-display.js";
|
|
|
6
6
|
function PortfolioHeader({
|
|
7
7
|
address,
|
|
8
8
|
score,
|
|
9
|
+
label = "Portfolio",
|
|
10
|
+
trailing,
|
|
9
11
|
className
|
|
10
12
|
}) {
|
|
11
13
|
const scoreChip = score && /* @__PURE__ */ jsxs("div", { className: "rounded-full bg-gradient-to-r from-brand-orange to-brand-maeve px-3.5 py-1.5 flex items-baseline gap-2 whitespace-nowrap text-white", children: [
|
|
@@ -17,7 +19,7 @@ function PortfolioHeader({
|
|
|
17
19
|
] });
|
|
18
20
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-4", className), children: [
|
|
19
21
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
20
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children:
|
|
22
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: label }),
|
|
21
23
|
/* @__PURE__ */ jsx(
|
|
22
24
|
AddressDisplay,
|
|
23
25
|
{
|
|
@@ -27,6 +29,7 @@ function PortfolioHeader({
|
|
|
27
29
|
}
|
|
28
30
|
)
|
|
29
31
|
] }),
|
|
32
|
+
trailing,
|
|
30
33
|
score?.href ? /* @__PURE__ */ jsx(Link, { href: score.href, className: "shrink-0 active:opacity-80", children: scoreChip }) : /* @__PURE__ */ jsx("div", { className: "shrink-0", children: scoreChip })
|
|
31
34
|
] });
|
|
32
35
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/portfolio-header.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { AddressDisplay } from \"./address-display.js\";\n\nexport interface PortfolioHeaderScore {\n\n levelName: string;\n totalXp: number;\n\n href?: string;\n}\n\nexport interface PortfolioHeaderProps {\n address: string;\n score?: PortfolioHeaderScore | null;\n className?: string;\n}\n\nexport function PortfolioHeader({\n address,\n score,\n className,\n}: PortfolioHeaderProps) {\n const scoreChip = score && (\n <div className=\"rounded-full bg-gradient-to-r from-brand-orange to-brand-maeve px-3.5 py-1.5 flex items-baseline gap-2 whitespace-nowrap text-white\">\n <span className=\"text-sm font-bold\">\n {score.levelName}\n </span>\n <span className=\"text-xs font-medium text-white/80 tabular-nums\">\n {score.totalXp.toLocaleString()} XP\n </span>\n </div>\n );\n\n return (\n <div className={cn(\"flex items-center justify-between gap-4\", className)}>\n <div className=\"min-w-0\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n
|
|
1
|
+
{"version":3,"sources":["../../src/components/portfolio-header.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport Link from \"./link.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { AddressDisplay } from \"./address-display.js\";\n\nexport interface PortfolioHeaderScore {\n\n levelName: string;\n totalXp: number;\n\n href?: string;\n}\n\nexport interface PortfolioHeaderProps {\n address: string;\n score?: PortfolioHeaderScore | null;\n label?: string;\n trailing?: ReactNode;\n className?: string;\n}\n\nexport function PortfolioHeader({\n address,\n score,\n label = \"Portfolio\",\n trailing,\n className,\n}: PortfolioHeaderProps) {\n const scoreChip = score && (\n <div className=\"rounded-full bg-gradient-to-r from-brand-orange to-brand-maeve px-3.5 py-1.5 flex items-baseline gap-2 whitespace-nowrap text-white\">\n <span className=\"text-sm font-bold\">\n {score.levelName}\n </span>\n <span className=\"text-xs font-medium text-white/80 tabular-nums\">\n {score.totalXp.toLocaleString()} XP\n </span>\n </div>\n );\n\n return (\n <div className={cn(\"flex items-center justify-between gap-4\", className)}>\n <div className=\"min-w-0\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n {label}\n </p>\n <AddressDisplay\n address={address}\n chars={6}\n className=\"text-2xl sm:text-3xl font-black tracking-tight text-foreground\"\n />\n </div>\n {trailing}\n {score?.href ? (\n <Link href={score.href} className=\"shrink-0 active:opacity-80\">\n {scoreChip}\n </Link>\n ) : (\n <div className=\"shrink-0\">{scoreChip}</div>\n )}\n </div>\n );\n}\n"],"mappings":";AAgCM,cAGA,YAHA;AA7BN,OAAO,UAAU;AACjB,SAAS,UAAU;AACnB,SAAS,sBAAsB;AAkBxB,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,YAAY,SAChB,qBAAC,SAAI,WAAU,uIACb;AAAA,wBAAC,UAAK,WAAU,qBACb,gBAAM,WACT;AAAA,IACA,qBAAC,UAAK,WAAU,kDACb;AAAA,YAAM,QAAQ,eAAe;AAAA,MAAE;AAAA,OAClC;AAAA,KACF;AAGF,SACE,qBAAC,SAAI,WAAW,GAAG,2CAA2C,SAAS,GACrE;AAAA,yBAAC,SAAI,WAAU,WACb;AAAA,0BAAC,OAAE,WAAU,6CACV,iBACH;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,OAAO;AAAA,UACP,WAAU;AAAA;AAAA,MACZ;AAAA,OACF;AAAA,IACC;AAAA,IACA,OAAO,OACN,oBAAC,QAAK,MAAM,MAAM,MAAM,WAAU,8BAC/B,qBACH,IAEA,oBAAC,SAAI,WAAU,YAAY,qBAAU;AAAA,KAEzC;AAEJ;","names":[]}
|
|
@@ -51,7 +51,7 @@ var import_use_orders = require("../utils/use-orders.js");
|
|
|
51
51
|
const VIEWS = {
|
|
52
52
|
listings: {
|
|
53
53
|
emptyTitle: "No active listings",
|
|
54
|
-
emptyDescription: "
|
|
54
|
+
emptyDescription: "Sell an asset to get started.",
|
|
55
55
|
emptyCta: { label: "Browse assets", href: "/portfolio/assets" },
|
|
56
56
|
emptyIcon: import_lucide_react.Tag,
|
|
57
57
|
priceLabel: "Price",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/portfolio-orders.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { assetHref, profileHref, type ApiOrder } from \"@medialane/sdk\";\nimport { ArrowLeftRight, ExternalLink, HandCoins, Inbox, Tag } from \"lucide-react\";\nimport Image from \"./image.js\";\nimport Link from \"./link.js\";\nimport { Badge } from \"./badge.js\";\nimport { Button } from \"./button.js\";\nimport { EmptyOrError } from \"./empty-or-error.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { isExpired, timeUntil } from \"../utils/time.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\nimport { getSeenOffers } from \"../utils/seen-offers.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\nimport { useCounterOffers, useReceivedOffers, useUserOrders } from \"../utils/use-orders.js\";\n\nexport type PortfolioOrdersView = \"listings\" | \"offers\" | \"received\" | \"counter-offers\";\n\nexport interface PortfolioOrderActions {\n isProcessing: boolean;\n cancel?: (order: ApiOrder) => void;\n accept?: (order: ApiOrder, original?: ApiOrder) => void;\n counter?: (order: ApiOrder) => void;\n dialogs?: ReactNode;\n}\n\nexport type UsePortfolioOrderActions = (context: { view: PortfolioOrdersView; refresh: () => void }) => PortfolioOrderActions;\n\nconst VIEWS = {\n listings: {\n emptyTitle: \"No active listings\",\n emptyDescription: \"List an asset to start selling.\",\n emptyCta: { label: \"Browse assets\", href: \"/portfolio/assets\" },\n emptyIcon: Tag,\n priceLabel: \"Price\",\n count: (n: number) => `${n} active listing${n === 1 ? \"\" : \"s\"}`,\n },\n offers: {\n emptyTitle: \"No offers sent yet\",\n emptyDescription: \"Browse the marketplace to make offers on assets.\",\n emptyCta: { label: \"Browse marketplace\", href: \"/marketplace\" },\n emptyIcon: HandCoins,\n priceLabel: \"Offer price\",\n count: (n: number) => `${n} active offer${n === 1 ? \"\" : \"s\"}`,\n },\n received: {\n emptyTitle: \"No offers received yet\",\n emptyDescription: \"When someone makes an offer on your asset, it will appear here.\",\n emptyCta: undefined,\n emptyIcon: Inbox,\n priceLabel: \"Offer price\",\n count: null,\n },\n \"counter-offers\": {\n emptyTitle: \"No counter-offers yet\",\n emptyDescription: \"When a seller responds to your bid with a counter-offer, it will appear here.\",\n emptyCta: undefined,\n emptyIcon: Inbox,\n priceLabel: \"Their price / Your bid\",\n count: null,\n },\n} as const;\n\nconst isNft = (o: ApiOrder) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\";\n\nfunction expiryOf(order: ApiOrder) {\n const expired = isExpired(order.endTime);\n const urgent = !expired && Number(order.endTime) * 1000 - Date.now() < 86400000;\n return { label: timeUntil(order.endTime), expired, urgent };\n}\n\nfunction price(o: ApiOrder) {\n return `${formatDisplayPrice(o.price.formatted)} ${o.price.currency ?? \"\"}`.trim();\n}\n\nfunction OrderRow({ order, subtitle, from, priceCell, actions, isNew }: {\n order: ApiOrder;\n subtitle: ReactNode;\n from?: string;\n priceCell: ReactNode;\n actions: ReactNode;\n isNew?: boolean;\n}) {\n const resolveImage = useImageUrl();\n const name = order.token?.name || `#${order.nftTokenId}`;\n const image = resolveImage(order.token?.image, { width: 96 });\n const expiry = expiryOf(order);\n return (\n <div data-new={isNew ? \"true\" : undefined} className={cn(\"flex items-center gap-4 px-4 py-3 transition-colors hover:bg-muted/40\", isNew && \"border-l-2 border-primary\")}>\n <div className=\"relative h-12 w-12 shrink-0 overflow-hidden rounded-lg border border-border bg-muted\">\n {image && <Image src={image} alt={name} fill sizes=\"48px\" unoptimized className=\"object-cover\" />}\n </div>\n <div className=\"min-w-0 flex-1\">\n <Link href={assetHref(\"STARKNET\", order.nftContract, order.nftTokenId)} className=\"block truncate text-sm font-medium transition-colors hover:text-primary\">\n {name}\n </Link>\n {subtitle}\n </div>\n {from && (\n <div className=\"hidden shrink-0 sm:block\">\n <Link href={profileHref({ address: from })} className=\"text-xs tabular-nums text-muted-foreground transition-colors hover:text-foreground\">\n {from.slice(0, 6)}…{from.slice(-4)}\n </Link>\n </div>\n )}\n <div className=\"hidden shrink-0 text-right sm:block\">{priceCell}</div>\n <div className=\"hidden shrink-0 md:block\">\n <p className={cn(\"text-sm\", expiry.expired && \"text-muted-foreground\", expiry.urgent && \"font-medium text-destructive\")}>{expiry.label}</p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {order.txHash?.created && (\n <a href={`${EXPLORER_URL}/tx/${order.txHash.created}`} target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"View on explorer\" className=\"inline-flex h-7 w-7 items-center justify-center rounded-md transition-colors hover:bg-muted\">\n <ExternalLink className=\"h-3.5 w-3.5 text-muted-foreground\" />\n </a>\n )}\n {actions}\n </div>\n </div>\n );\n}\n\nfunction PriceText({ order }: { order: ApiOrder }) {\n return (\n <p className=\"text-sm font-semibold\">\n {formatDisplayPrice(order.price.formatted)} <span className=\"text-xs font-normal text-muted-foreground\">{order.price.currency}</span>\n </p>\n );\n}\n\nfunction CounterOfferRow({ bid, actions }: { bid: ApiOrder; actions: PortfolioOrderActions }) {\n const { counterOffers } = useCounterOffers({ originalOrderHash: bid.orderHash });\n const counter = counterOffers[0];\n if (!counter) return null;\n const closed = counter.status !== \"ACTIVE\" || isExpired(counter.endTime);\n return (\n <OrderRow\n order={counter}\n subtitle={\n <Badge variant=\"secondary\" className=\"mt-0.5 h-4 px-1.5 text-2xs\">\n <ArrowLeftRight className=\"mr-1 h-2.5 w-2.5\" />\n Counter-offer\n </Badge>\n }\n priceCell={\n <div className=\"flex flex-col items-end gap-0.5\">\n <p className=\"text-xs text-muted-foreground line-through\">{price(bid)}</p>\n <p className=\"text-sm font-semibold\">{counter.price.formatted ? price(counter) : \"—\"}</p>\n </div>\n }\n actions={\n closed ? (\n <span className=\"text-xs text-muted-foreground\">{counter.status !== \"ACTIVE\" ? counter.status.toLowerCase() : \"Expired\"}</span>\n ) : (\n <Button size=\"sm\" className=\"h-8 text-xs\" disabled={actions.isProcessing} onClick={() => actions.accept?.(counter, bid)}>\n Accept\n </Button>\n )\n }\n />\n );\n}\n\nexport function PortfolioOrders({ view, address, useActions }: {\n view: PortfolioOrdersView;\n address: string;\n useActions: UsePortfolioOrderActions;\n}) {\n const mine = useUserOrders(view === \"received\" ? null : address);\n const received = useReceivedOffers(view === \"received\" ? address : null);\n const source = view === \"received\" ? received : mine;\n const actions = useActions({ view, refresh: () => void source.mutate() });\n const config = VIEWS[view];\n\n const orders =\n view === \"listings\" ? mine.orders.filter((o) => isNft(o) && o.status === \"ACTIVE\")\n : view === \"offers\" ? mine.orders.filter((o) => o.offer.itemType === \"ERC20\" && o.status === \"ACTIVE\")\n : view === \"counter-offers\" ? mine.orders.filter((o) => o.offer.itemType === \"ERC20\" && isSameAddress(o.offerer, address) && o.hasActiveCounterOffer === true)\n : received.orders;\n\n const seen = view === \"received\" ? getSeenOffers() : null;\n const EmptyIcon = config.emptyIcon;\n\n return (\n <>\n <EmptyOrError\n isLoading={source.isLoading}\n error={source.error}\n isEmpty={orders.length === 0}\n onRetry={() => void source.mutate()}\n emptyTitle={config.emptyTitle}\n emptyDescription={config.emptyDescription}\n emptyCta={config.emptyCta}\n emptyIcon={<EmptyIcon className=\"h-7 w-7 text-muted-foreground\" />}\n skeletonCount={3}\n >\n <div className=\"space-y-1\">\n {config.count && orders.length > 0 && <p className=\"pb-2 text-sm text-muted-foreground\">{config.count(orders.length)}</p>}\n <div className=\"overflow-hidden rounded-lg border border-border\">\n <div className=\"flex items-center gap-4 border-b border-border bg-muted/30 px-4 py-2 text-xs uppercase tracking-wide text-muted-foreground\">\n <div className=\"w-12 shrink-0\" />\n <div className=\"flex-1\">Asset</div>\n {view === \"received\" && <div className=\"hidden w-28 sm:block\">From</div>}\n <div className={cn(\"hidden text-right sm:block\", view === \"counter-offers\" ? \"w-40\" : \"w-24\")}>{config.priceLabel}</div>\n <div className=\"hidden w-28 md:block\">Expires</div>\n <div className=\"w-24 text-right\">Actions</div>\n </div>\n {view === \"counter-offers\"\n ? orders.map((bid) => <CounterOfferRow key={bid.orderHash} bid={bid} actions={actions} />)\n : orders.map((order) => (\n <OrderRow\n key={order.orderHash}\n order={order}\n isNew={seen ? !seen.has(order.orderHash) : false}\n from={view === \"received\" ? order.offerer : undefined}\n subtitle={<span className=\"text-xs text-muted-foreground\">{view === \"received\" ? \"Received offer\" : view === \"offers\" ? \"Offer\" : \"Active\"}</span>}\n priceCell={<PriceText order={order} />}\n actions={\n view === \"received\" ? (\n <>\n <Button size=\"sm\" variant=\"outline\" className=\"h-8 text-xs\" disabled={actions.isProcessing} onClick={() => actions.counter?.(order)}>Counter</Button>\n <Button size=\"sm\" className=\"h-8 text-xs\" disabled={actions.isProcessing} onClick={() => actions.accept?.(order)}>Accept</Button>\n </>\n ) : (\n <Button size=\"sm\" variant=\"outline\" className=\"h-8 text-xs text-destructive hover:text-destructive\" disabled={actions.isProcessing} onClick={() => actions.cancel?.(order)}>Cancel</Button>\n )\n }\n />\n ))}\n </div>\n </div>\n </EmptyOrError>\n {actions.dialogs}\n </>\n );\n}\n"],"mappings":"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","names":["Image","Link"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/portfolio-orders.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { assetHref, profileHref, type ApiOrder } from \"@medialane/sdk\";\nimport { ArrowLeftRight, ExternalLink, HandCoins, Inbox, Tag } from \"lucide-react\";\nimport Image from \"./image.js\";\nimport Link from \"./link.js\";\nimport { Badge } from \"./badge.js\";\nimport { Button } from \"./button.js\";\nimport { EmptyOrError } from \"./empty-or-error.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { isExpired, timeUntil } from \"../utils/time.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\nimport { getSeenOffers } from \"../utils/seen-offers.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\nimport { useCounterOffers, useReceivedOffers, useUserOrders } from \"../utils/use-orders.js\";\n\nexport type PortfolioOrdersView = \"listings\" | \"offers\" | \"received\" | \"counter-offers\";\n\nexport interface PortfolioOrderActions {\n isProcessing: boolean;\n cancel?: (order: ApiOrder) => void;\n accept?: (order: ApiOrder, original?: ApiOrder) => void;\n counter?: (order: ApiOrder) => void;\n dialogs?: ReactNode;\n}\n\nexport type UsePortfolioOrderActions = (context: { view: PortfolioOrdersView; refresh: () => void }) => PortfolioOrderActions;\n\nconst VIEWS = {\n listings: {\n emptyTitle: \"No active listings\",\n emptyDescription: \"Sell an asset to get started.\",\n emptyCta: { label: \"Browse assets\", href: \"/portfolio/assets\" },\n emptyIcon: Tag,\n priceLabel: \"Price\",\n count: (n: number) => `${n} active listing${n === 1 ? \"\" : \"s\"}`,\n },\n offers: {\n emptyTitle: \"No offers sent yet\",\n emptyDescription: \"Browse the marketplace to make offers on assets.\",\n emptyCta: { label: \"Browse marketplace\", href: \"/marketplace\" },\n emptyIcon: HandCoins,\n priceLabel: \"Offer price\",\n count: (n: number) => `${n} active offer${n === 1 ? \"\" : \"s\"}`,\n },\n received: {\n emptyTitle: \"No offers received yet\",\n emptyDescription: \"When someone makes an offer on your asset, it will appear here.\",\n emptyCta: undefined,\n emptyIcon: Inbox,\n priceLabel: \"Offer price\",\n count: null,\n },\n \"counter-offers\": {\n emptyTitle: \"No counter-offers yet\",\n emptyDescription: \"When a seller responds to your bid with a counter-offer, it will appear here.\",\n emptyCta: undefined,\n emptyIcon: Inbox,\n priceLabel: \"Their price / Your bid\",\n count: null,\n },\n} as const;\n\nconst isNft = (o: ApiOrder) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\";\n\nfunction expiryOf(order: ApiOrder) {\n const expired = isExpired(order.endTime);\n const urgent = !expired && Number(order.endTime) * 1000 - Date.now() < 86400000;\n return { label: timeUntil(order.endTime), expired, urgent };\n}\n\nfunction price(o: ApiOrder) {\n return `${formatDisplayPrice(o.price.formatted)} ${o.price.currency ?? \"\"}`.trim();\n}\n\nfunction OrderRow({ order, subtitle, from, priceCell, actions, isNew }: {\n order: ApiOrder;\n subtitle: ReactNode;\n from?: string;\n priceCell: ReactNode;\n actions: ReactNode;\n isNew?: boolean;\n}) {\n const resolveImage = useImageUrl();\n const name = order.token?.name || `#${order.nftTokenId}`;\n const image = resolveImage(order.token?.image, { width: 96 });\n const expiry = expiryOf(order);\n return (\n <div data-new={isNew ? \"true\" : undefined} className={cn(\"flex items-center gap-4 px-4 py-3 transition-colors hover:bg-muted/40\", isNew && \"border-l-2 border-primary\")}>\n <div className=\"relative h-12 w-12 shrink-0 overflow-hidden rounded-lg border border-border bg-muted\">\n {image && <Image src={image} alt={name} fill sizes=\"48px\" unoptimized className=\"object-cover\" />}\n </div>\n <div className=\"min-w-0 flex-1\">\n <Link href={assetHref(\"STARKNET\", order.nftContract, order.nftTokenId)} className=\"block truncate text-sm font-medium transition-colors hover:text-primary\">\n {name}\n </Link>\n {subtitle}\n </div>\n {from && (\n <div className=\"hidden shrink-0 sm:block\">\n <Link href={profileHref({ address: from })} className=\"text-xs tabular-nums text-muted-foreground transition-colors hover:text-foreground\">\n {from.slice(0, 6)}…{from.slice(-4)}\n </Link>\n </div>\n )}\n <div className=\"hidden shrink-0 text-right sm:block\">{priceCell}</div>\n <div className=\"hidden shrink-0 md:block\">\n <p className={cn(\"text-sm\", expiry.expired && \"text-muted-foreground\", expiry.urgent && \"font-medium text-destructive\")}>{expiry.label}</p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {order.txHash?.created && (\n <a href={`${EXPLORER_URL}/tx/${order.txHash.created}`} target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"View on explorer\" className=\"inline-flex h-7 w-7 items-center justify-center rounded-md transition-colors hover:bg-muted\">\n <ExternalLink className=\"h-3.5 w-3.5 text-muted-foreground\" />\n </a>\n )}\n {actions}\n </div>\n </div>\n );\n}\n\nfunction PriceText({ order }: { order: ApiOrder }) {\n return (\n <p className=\"text-sm font-semibold\">\n {formatDisplayPrice(order.price.formatted)} <span className=\"text-xs font-normal text-muted-foreground\">{order.price.currency}</span>\n </p>\n );\n}\n\nfunction CounterOfferRow({ bid, actions }: { bid: ApiOrder; actions: PortfolioOrderActions }) {\n const { counterOffers } = useCounterOffers({ originalOrderHash: bid.orderHash });\n const counter = counterOffers[0];\n if (!counter) return null;\n const closed = counter.status !== \"ACTIVE\" || isExpired(counter.endTime);\n return (\n <OrderRow\n order={counter}\n subtitle={\n <Badge variant=\"secondary\" className=\"mt-0.5 h-4 px-1.5 text-2xs\">\n <ArrowLeftRight className=\"mr-1 h-2.5 w-2.5\" />\n Counter-offer\n </Badge>\n }\n priceCell={\n <div className=\"flex flex-col items-end gap-0.5\">\n <p className=\"text-xs text-muted-foreground line-through\">{price(bid)}</p>\n <p className=\"text-sm font-semibold\">{counter.price.formatted ? price(counter) : \"—\"}</p>\n </div>\n }\n actions={\n closed ? (\n <span className=\"text-xs text-muted-foreground\">{counter.status !== \"ACTIVE\" ? counter.status.toLowerCase() : \"Expired\"}</span>\n ) : (\n <Button size=\"sm\" className=\"h-8 text-xs\" disabled={actions.isProcessing} onClick={() => actions.accept?.(counter, bid)}>\n Accept\n </Button>\n )\n }\n />\n );\n}\n\nexport function PortfolioOrders({ view, address, useActions }: {\n view: PortfolioOrdersView;\n address: string;\n useActions: UsePortfolioOrderActions;\n}) {\n const mine = useUserOrders(view === \"received\" ? null : address);\n const received = useReceivedOffers(view === \"received\" ? address : null);\n const source = view === \"received\" ? received : mine;\n const actions = useActions({ view, refresh: () => void source.mutate() });\n const config = VIEWS[view];\n\n const orders =\n view === \"listings\" ? mine.orders.filter((o) => isNft(o) && o.status === \"ACTIVE\")\n : view === \"offers\" ? mine.orders.filter((o) => o.offer.itemType === \"ERC20\" && o.status === \"ACTIVE\")\n : view === \"counter-offers\" ? mine.orders.filter((o) => o.offer.itemType === \"ERC20\" && isSameAddress(o.offerer, address) && o.hasActiveCounterOffer === true)\n : received.orders;\n\n const seen = view === \"received\" ? getSeenOffers() : null;\n const EmptyIcon = config.emptyIcon;\n\n return (\n <>\n <EmptyOrError\n isLoading={source.isLoading}\n error={source.error}\n isEmpty={orders.length === 0}\n onRetry={() => void source.mutate()}\n emptyTitle={config.emptyTitle}\n emptyDescription={config.emptyDescription}\n emptyCta={config.emptyCta}\n emptyIcon={<EmptyIcon className=\"h-7 w-7 text-muted-foreground\" />}\n skeletonCount={3}\n >\n <div className=\"space-y-1\">\n {config.count && orders.length > 0 && <p className=\"pb-2 text-sm text-muted-foreground\">{config.count(orders.length)}</p>}\n <div className=\"overflow-hidden rounded-lg border border-border\">\n <div className=\"flex items-center gap-4 border-b border-border bg-muted/30 px-4 py-2 text-xs uppercase tracking-wide text-muted-foreground\">\n <div className=\"w-12 shrink-0\" />\n <div className=\"flex-1\">Asset</div>\n {view === \"received\" && <div className=\"hidden w-28 sm:block\">From</div>}\n <div className={cn(\"hidden text-right sm:block\", view === \"counter-offers\" ? \"w-40\" : \"w-24\")}>{config.priceLabel}</div>\n <div className=\"hidden w-28 md:block\">Expires</div>\n <div className=\"w-24 text-right\">Actions</div>\n </div>\n {view === \"counter-offers\"\n ? orders.map((bid) => <CounterOfferRow key={bid.orderHash} bid={bid} actions={actions} />)\n : orders.map((order) => (\n <OrderRow\n key={order.orderHash}\n order={order}\n isNew={seen ? !seen.has(order.orderHash) : false}\n from={view === \"received\" ? order.offerer : undefined}\n subtitle={<span className=\"text-xs text-muted-foreground\">{view === \"received\" ? \"Received offer\" : view === \"offers\" ? \"Offer\" : \"Active\"}</span>}\n priceCell={<PriceText order={order} />}\n actions={\n view === \"received\" ? (\n <>\n <Button size=\"sm\" variant=\"outline\" className=\"h-8 text-xs\" disabled={actions.isProcessing} onClick={() => actions.counter?.(order)}>Counter</Button>\n <Button size=\"sm\" className=\"h-8 text-xs\" disabled={actions.isProcessing} onClick={() => actions.accept?.(order)}>Accept</Button>\n </>\n ) : (\n <Button size=\"sm\" variant=\"outline\" className=\"h-8 text-xs text-destructive hover:text-destructive\" disabled={actions.isProcessing} onClick={() => actions.cancel?.(order)}>Cancel</Button>\n )\n }\n />\n ))}\n </div>\n </div>\n </EmptyOrError>\n {actions.dialogs}\n </>\n );\n}\n"],"mappings":"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","names":["Image","Link"]}
|
|
@@ -18,7 +18,7 @@ import { useCounterOffers, useReceivedOffers, useUserOrders } from "../utils/use
|
|
|
18
18
|
const VIEWS = {
|
|
19
19
|
listings: {
|
|
20
20
|
emptyTitle: "No active listings",
|
|
21
|
-
emptyDescription: "
|
|
21
|
+
emptyDescription: "Sell an asset to get started.",
|
|
22
22
|
emptyCta: { label: "Browse assets", href: "/portfolio/assets" },
|
|
23
23
|
emptyIcon: Tag,
|
|
24
24
|
priceLabel: "Price",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/portfolio-orders.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { assetHref, profileHref, type ApiOrder } from \"@medialane/sdk\";\nimport { ArrowLeftRight, ExternalLink, HandCoins, Inbox, Tag } from \"lucide-react\";\nimport Image from \"./image.js\";\nimport Link from \"./link.js\";\nimport { Badge } from \"./badge.js\";\nimport { Button } from \"./button.js\";\nimport { EmptyOrError } from \"./empty-or-error.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { isExpired, timeUntil } from \"../utils/time.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\nimport { getSeenOffers } from \"../utils/seen-offers.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\nimport { useCounterOffers, useReceivedOffers, useUserOrders } from \"../utils/use-orders.js\";\n\nexport type PortfolioOrdersView = \"listings\" | \"offers\" | \"received\" | \"counter-offers\";\n\nexport interface PortfolioOrderActions {\n isProcessing: boolean;\n cancel?: (order: ApiOrder) => void;\n accept?: (order: ApiOrder, original?: ApiOrder) => void;\n counter?: (order: ApiOrder) => void;\n dialogs?: ReactNode;\n}\n\nexport type UsePortfolioOrderActions = (context: { view: PortfolioOrdersView; refresh: () => void }) => PortfolioOrderActions;\n\nconst VIEWS = {\n listings: {\n emptyTitle: \"No active listings\",\n emptyDescription: \"List an asset to start selling.\",\n emptyCta: { label: \"Browse assets\", href: \"/portfolio/assets\" },\n emptyIcon: Tag,\n priceLabel: \"Price\",\n count: (n: number) => `${n} active listing${n === 1 ? \"\" : \"s\"}`,\n },\n offers: {\n emptyTitle: \"No offers sent yet\",\n emptyDescription: \"Browse the marketplace to make offers on assets.\",\n emptyCta: { label: \"Browse marketplace\", href: \"/marketplace\" },\n emptyIcon: HandCoins,\n priceLabel: \"Offer price\",\n count: (n: number) => `${n} active offer${n === 1 ? \"\" : \"s\"}`,\n },\n received: {\n emptyTitle: \"No offers received yet\",\n emptyDescription: \"When someone makes an offer on your asset, it will appear here.\",\n emptyCta: undefined,\n emptyIcon: Inbox,\n priceLabel: \"Offer price\",\n count: null,\n },\n \"counter-offers\": {\n emptyTitle: \"No counter-offers yet\",\n emptyDescription: \"When a seller responds to your bid with a counter-offer, it will appear here.\",\n emptyCta: undefined,\n emptyIcon: Inbox,\n priceLabel: \"Their price / Your bid\",\n count: null,\n },\n} as const;\n\nconst isNft = (o: ApiOrder) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\";\n\nfunction expiryOf(order: ApiOrder) {\n const expired = isExpired(order.endTime);\n const urgent = !expired && Number(order.endTime) * 1000 - Date.now() < 86400000;\n return { label: timeUntil(order.endTime), expired, urgent };\n}\n\nfunction price(o: ApiOrder) {\n return `${formatDisplayPrice(o.price.formatted)} ${o.price.currency ?? \"\"}`.trim();\n}\n\nfunction OrderRow({ order, subtitle, from, priceCell, actions, isNew }: {\n order: ApiOrder;\n subtitle: ReactNode;\n from?: string;\n priceCell: ReactNode;\n actions: ReactNode;\n isNew?: boolean;\n}) {\n const resolveImage = useImageUrl();\n const name = order.token?.name || `#${order.nftTokenId}`;\n const image = resolveImage(order.token?.image, { width: 96 });\n const expiry = expiryOf(order);\n return (\n <div data-new={isNew ? \"true\" : undefined} className={cn(\"flex items-center gap-4 px-4 py-3 transition-colors hover:bg-muted/40\", isNew && \"border-l-2 border-primary\")}>\n <div className=\"relative h-12 w-12 shrink-0 overflow-hidden rounded-lg border border-border bg-muted\">\n {image && <Image src={image} alt={name} fill sizes=\"48px\" unoptimized className=\"object-cover\" />}\n </div>\n <div className=\"min-w-0 flex-1\">\n <Link href={assetHref(\"STARKNET\", order.nftContract, order.nftTokenId)} className=\"block truncate text-sm font-medium transition-colors hover:text-primary\">\n {name}\n </Link>\n {subtitle}\n </div>\n {from && (\n <div className=\"hidden shrink-0 sm:block\">\n <Link href={profileHref({ address: from })} className=\"text-xs tabular-nums text-muted-foreground transition-colors hover:text-foreground\">\n {from.slice(0, 6)}…{from.slice(-4)}\n </Link>\n </div>\n )}\n <div className=\"hidden shrink-0 text-right sm:block\">{priceCell}</div>\n <div className=\"hidden shrink-0 md:block\">\n <p className={cn(\"text-sm\", expiry.expired && \"text-muted-foreground\", expiry.urgent && \"font-medium text-destructive\")}>{expiry.label}</p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {order.txHash?.created && (\n <a href={`${EXPLORER_URL}/tx/${order.txHash.created}`} target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"View on explorer\" className=\"inline-flex h-7 w-7 items-center justify-center rounded-md transition-colors hover:bg-muted\">\n <ExternalLink className=\"h-3.5 w-3.5 text-muted-foreground\" />\n </a>\n )}\n {actions}\n </div>\n </div>\n );\n}\n\nfunction PriceText({ order }: { order: ApiOrder }) {\n return (\n <p className=\"text-sm font-semibold\">\n {formatDisplayPrice(order.price.formatted)} <span className=\"text-xs font-normal text-muted-foreground\">{order.price.currency}</span>\n </p>\n );\n}\n\nfunction CounterOfferRow({ bid, actions }: { bid: ApiOrder; actions: PortfolioOrderActions }) {\n const { counterOffers } = useCounterOffers({ originalOrderHash: bid.orderHash });\n const counter = counterOffers[0];\n if (!counter) return null;\n const closed = counter.status !== \"ACTIVE\" || isExpired(counter.endTime);\n return (\n <OrderRow\n order={counter}\n subtitle={\n <Badge variant=\"secondary\" className=\"mt-0.5 h-4 px-1.5 text-2xs\">\n <ArrowLeftRight className=\"mr-1 h-2.5 w-2.5\" />\n Counter-offer\n </Badge>\n }\n priceCell={\n <div className=\"flex flex-col items-end gap-0.5\">\n <p className=\"text-xs text-muted-foreground line-through\">{price(bid)}</p>\n <p className=\"text-sm font-semibold\">{counter.price.formatted ? price(counter) : \"—\"}</p>\n </div>\n }\n actions={\n closed ? (\n <span className=\"text-xs text-muted-foreground\">{counter.status !== \"ACTIVE\" ? counter.status.toLowerCase() : \"Expired\"}</span>\n ) : (\n <Button size=\"sm\" className=\"h-8 text-xs\" disabled={actions.isProcessing} onClick={() => actions.accept?.(counter, bid)}>\n Accept\n </Button>\n )\n }\n />\n );\n}\n\nexport function PortfolioOrders({ view, address, useActions }: {\n view: PortfolioOrdersView;\n address: string;\n useActions: UsePortfolioOrderActions;\n}) {\n const mine = useUserOrders(view === \"received\" ? null : address);\n const received = useReceivedOffers(view === \"received\" ? address : null);\n const source = view === \"received\" ? received : mine;\n const actions = useActions({ view, refresh: () => void source.mutate() });\n const config = VIEWS[view];\n\n const orders =\n view === \"listings\" ? mine.orders.filter((o) => isNft(o) && o.status === \"ACTIVE\")\n : view === \"offers\" ? mine.orders.filter((o) => o.offer.itemType === \"ERC20\" && o.status === \"ACTIVE\")\n : view === \"counter-offers\" ? mine.orders.filter((o) => o.offer.itemType === \"ERC20\" && isSameAddress(o.offerer, address) && o.hasActiveCounterOffer === true)\n : received.orders;\n\n const seen = view === \"received\" ? getSeenOffers() : null;\n const EmptyIcon = config.emptyIcon;\n\n return (\n <>\n <EmptyOrError\n isLoading={source.isLoading}\n error={source.error}\n isEmpty={orders.length === 0}\n onRetry={() => void source.mutate()}\n emptyTitle={config.emptyTitle}\n emptyDescription={config.emptyDescription}\n emptyCta={config.emptyCta}\n emptyIcon={<EmptyIcon className=\"h-7 w-7 text-muted-foreground\" />}\n skeletonCount={3}\n >\n <div className=\"space-y-1\">\n {config.count && orders.length > 0 && <p className=\"pb-2 text-sm text-muted-foreground\">{config.count(orders.length)}</p>}\n <div className=\"overflow-hidden rounded-lg border border-border\">\n <div className=\"flex items-center gap-4 border-b border-border bg-muted/30 px-4 py-2 text-xs uppercase tracking-wide text-muted-foreground\">\n <div className=\"w-12 shrink-0\" />\n <div className=\"flex-1\">Asset</div>\n {view === \"received\" && <div className=\"hidden w-28 sm:block\">From</div>}\n <div className={cn(\"hidden text-right sm:block\", view === \"counter-offers\" ? \"w-40\" : \"w-24\")}>{config.priceLabel}</div>\n <div className=\"hidden w-28 md:block\">Expires</div>\n <div className=\"w-24 text-right\">Actions</div>\n </div>\n {view === \"counter-offers\"\n ? orders.map((bid) => <CounterOfferRow key={bid.orderHash} bid={bid} actions={actions} />)\n : orders.map((order) => (\n <OrderRow\n key={order.orderHash}\n order={order}\n isNew={seen ? !seen.has(order.orderHash) : false}\n from={view === \"received\" ? order.offerer : undefined}\n subtitle={<span className=\"text-xs text-muted-foreground\">{view === \"received\" ? \"Received offer\" : view === \"offers\" ? \"Offer\" : \"Active\"}</span>}\n priceCell={<PriceText order={order} />}\n actions={\n view === \"received\" ? (\n <>\n <Button size=\"sm\" variant=\"outline\" className=\"h-8 text-xs\" disabled={actions.isProcessing} onClick={() => actions.counter?.(order)}>Counter</Button>\n <Button size=\"sm\" className=\"h-8 text-xs\" disabled={actions.isProcessing} onClick={() => actions.accept?.(order)}>Accept</Button>\n </>\n ) : (\n <Button size=\"sm\" variant=\"outline\" className=\"h-8 text-xs text-destructive hover:text-destructive\" disabled={actions.isProcessing} onClick={() => actions.cancel?.(order)}>Cancel</Button>\n )\n }\n />\n ))}\n </div>\n </div>\n </EmptyOrError>\n {actions.dialogs}\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/portfolio-orders.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { assetHref, profileHref, type ApiOrder } from \"@medialane/sdk\";\nimport { ArrowLeftRight, ExternalLink, HandCoins, Inbox, Tag } from \"lucide-react\";\nimport Image from \"./image.js\";\nimport Link from \"./link.js\";\nimport { Badge } from \"./badge.js\";\nimport { Button } from \"./button.js\";\nimport { EmptyOrError } from \"./empty-or-error.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { EXPLORER_URL } from \"../utils/explorer-url.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { isExpired, timeUntil } from \"../utils/time.js\";\nimport { isSameAddress } from \"../utils/same-address.js\";\nimport { getSeenOffers } from \"../utils/seen-offers.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\nimport { useCounterOffers, useReceivedOffers, useUserOrders } from \"../utils/use-orders.js\";\n\nexport type PortfolioOrdersView = \"listings\" | \"offers\" | \"received\" | \"counter-offers\";\n\nexport interface PortfolioOrderActions {\n isProcessing: boolean;\n cancel?: (order: ApiOrder) => void;\n accept?: (order: ApiOrder, original?: ApiOrder) => void;\n counter?: (order: ApiOrder) => void;\n dialogs?: ReactNode;\n}\n\nexport type UsePortfolioOrderActions = (context: { view: PortfolioOrdersView; refresh: () => void }) => PortfolioOrderActions;\n\nconst VIEWS = {\n listings: {\n emptyTitle: \"No active listings\",\n emptyDescription: \"Sell an asset to get started.\",\n emptyCta: { label: \"Browse assets\", href: \"/portfolio/assets\" },\n emptyIcon: Tag,\n priceLabel: \"Price\",\n count: (n: number) => `${n} active listing${n === 1 ? \"\" : \"s\"}`,\n },\n offers: {\n emptyTitle: \"No offers sent yet\",\n emptyDescription: \"Browse the marketplace to make offers on assets.\",\n emptyCta: { label: \"Browse marketplace\", href: \"/marketplace\" },\n emptyIcon: HandCoins,\n priceLabel: \"Offer price\",\n count: (n: number) => `${n} active offer${n === 1 ? \"\" : \"s\"}`,\n },\n received: {\n emptyTitle: \"No offers received yet\",\n emptyDescription: \"When someone makes an offer on your asset, it will appear here.\",\n emptyCta: undefined,\n emptyIcon: Inbox,\n priceLabel: \"Offer price\",\n count: null,\n },\n \"counter-offers\": {\n emptyTitle: \"No counter-offers yet\",\n emptyDescription: \"When a seller responds to your bid with a counter-offer, it will appear here.\",\n emptyCta: undefined,\n emptyIcon: Inbox,\n priceLabel: \"Their price / Your bid\",\n count: null,\n },\n} as const;\n\nconst isNft = (o: ApiOrder) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\";\n\nfunction expiryOf(order: ApiOrder) {\n const expired = isExpired(order.endTime);\n const urgent = !expired && Number(order.endTime) * 1000 - Date.now() < 86400000;\n return { label: timeUntil(order.endTime), expired, urgent };\n}\n\nfunction price(o: ApiOrder) {\n return `${formatDisplayPrice(o.price.formatted)} ${o.price.currency ?? \"\"}`.trim();\n}\n\nfunction OrderRow({ order, subtitle, from, priceCell, actions, isNew }: {\n order: ApiOrder;\n subtitle: ReactNode;\n from?: string;\n priceCell: ReactNode;\n actions: ReactNode;\n isNew?: boolean;\n}) {\n const resolveImage = useImageUrl();\n const name = order.token?.name || `#${order.nftTokenId}`;\n const image = resolveImage(order.token?.image, { width: 96 });\n const expiry = expiryOf(order);\n return (\n <div data-new={isNew ? \"true\" : undefined} className={cn(\"flex items-center gap-4 px-4 py-3 transition-colors hover:bg-muted/40\", isNew && \"border-l-2 border-primary\")}>\n <div className=\"relative h-12 w-12 shrink-0 overflow-hidden rounded-lg border border-border bg-muted\">\n {image && <Image src={image} alt={name} fill sizes=\"48px\" unoptimized className=\"object-cover\" />}\n </div>\n <div className=\"min-w-0 flex-1\">\n <Link href={assetHref(\"STARKNET\", order.nftContract, order.nftTokenId)} className=\"block truncate text-sm font-medium transition-colors hover:text-primary\">\n {name}\n </Link>\n {subtitle}\n </div>\n {from && (\n <div className=\"hidden shrink-0 sm:block\">\n <Link href={profileHref({ address: from })} className=\"text-xs tabular-nums text-muted-foreground transition-colors hover:text-foreground\">\n {from.slice(0, 6)}…{from.slice(-4)}\n </Link>\n </div>\n )}\n <div className=\"hidden shrink-0 text-right sm:block\">{priceCell}</div>\n <div className=\"hidden shrink-0 md:block\">\n <p className={cn(\"text-sm\", expiry.expired && \"text-muted-foreground\", expiry.urgent && \"font-medium text-destructive\")}>{expiry.label}</p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {order.txHash?.created && (\n <a href={`${EXPLORER_URL}/tx/${order.txHash.created}`} target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"View on explorer\" className=\"inline-flex h-7 w-7 items-center justify-center rounded-md transition-colors hover:bg-muted\">\n <ExternalLink className=\"h-3.5 w-3.5 text-muted-foreground\" />\n </a>\n )}\n {actions}\n </div>\n </div>\n );\n}\n\nfunction PriceText({ order }: { order: ApiOrder }) {\n return (\n <p className=\"text-sm font-semibold\">\n {formatDisplayPrice(order.price.formatted)} <span className=\"text-xs font-normal text-muted-foreground\">{order.price.currency}</span>\n </p>\n );\n}\n\nfunction CounterOfferRow({ bid, actions }: { bid: ApiOrder; actions: PortfolioOrderActions }) {\n const { counterOffers } = useCounterOffers({ originalOrderHash: bid.orderHash });\n const counter = counterOffers[0];\n if (!counter) return null;\n const closed = counter.status !== \"ACTIVE\" || isExpired(counter.endTime);\n return (\n <OrderRow\n order={counter}\n subtitle={\n <Badge variant=\"secondary\" className=\"mt-0.5 h-4 px-1.5 text-2xs\">\n <ArrowLeftRight className=\"mr-1 h-2.5 w-2.5\" />\n Counter-offer\n </Badge>\n }\n priceCell={\n <div className=\"flex flex-col items-end gap-0.5\">\n <p className=\"text-xs text-muted-foreground line-through\">{price(bid)}</p>\n <p className=\"text-sm font-semibold\">{counter.price.formatted ? price(counter) : \"—\"}</p>\n </div>\n }\n actions={\n closed ? (\n <span className=\"text-xs text-muted-foreground\">{counter.status !== \"ACTIVE\" ? counter.status.toLowerCase() : \"Expired\"}</span>\n ) : (\n <Button size=\"sm\" className=\"h-8 text-xs\" disabled={actions.isProcessing} onClick={() => actions.accept?.(counter, bid)}>\n Accept\n </Button>\n )\n }\n />\n );\n}\n\nexport function PortfolioOrders({ view, address, useActions }: {\n view: PortfolioOrdersView;\n address: string;\n useActions: UsePortfolioOrderActions;\n}) {\n const mine = useUserOrders(view === \"received\" ? null : address);\n const received = useReceivedOffers(view === \"received\" ? address : null);\n const source = view === \"received\" ? received : mine;\n const actions = useActions({ view, refresh: () => void source.mutate() });\n const config = VIEWS[view];\n\n const orders =\n view === \"listings\" ? mine.orders.filter((o) => isNft(o) && o.status === \"ACTIVE\")\n : view === \"offers\" ? mine.orders.filter((o) => o.offer.itemType === \"ERC20\" && o.status === \"ACTIVE\")\n : view === \"counter-offers\" ? mine.orders.filter((o) => o.offer.itemType === \"ERC20\" && isSameAddress(o.offerer, address) && o.hasActiveCounterOffer === true)\n : received.orders;\n\n const seen = view === \"received\" ? getSeenOffers() : null;\n const EmptyIcon = config.emptyIcon;\n\n return (\n <>\n <EmptyOrError\n isLoading={source.isLoading}\n error={source.error}\n isEmpty={orders.length === 0}\n onRetry={() => void source.mutate()}\n emptyTitle={config.emptyTitle}\n emptyDescription={config.emptyDescription}\n emptyCta={config.emptyCta}\n emptyIcon={<EmptyIcon className=\"h-7 w-7 text-muted-foreground\" />}\n skeletonCount={3}\n >\n <div className=\"space-y-1\">\n {config.count && orders.length > 0 && <p className=\"pb-2 text-sm text-muted-foreground\">{config.count(orders.length)}</p>}\n <div className=\"overflow-hidden rounded-lg border border-border\">\n <div className=\"flex items-center gap-4 border-b border-border bg-muted/30 px-4 py-2 text-xs uppercase tracking-wide text-muted-foreground\">\n <div className=\"w-12 shrink-0\" />\n <div className=\"flex-1\">Asset</div>\n {view === \"received\" && <div className=\"hidden w-28 sm:block\">From</div>}\n <div className={cn(\"hidden text-right sm:block\", view === \"counter-offers\" ? \"w-40\" : \"w-24\")}>{config.priceLabel}</div>\n <div className=\"hidden w-28 md:block\">Expires</div>\n <div className=\"w-24 text-right\">Actions</div>\n </div>\n {view === \"counter-offers\"\n ? orders.map((bid) => <CounterOfferRow key={bid.orderHash} bid={bid} actions={actions} />)\n : orders.map((order) => (\n <OrderRow\n key={order.orderHash}\n order={order}\n isNew={seen ? !seen.has(order.orderHash) : false}\n from={view === \"received\" ? order.offerer : undefined}\n subtitle={<span className=\"text-xs text-muted-foreground\">{view === \"received\" ? \"Received offer\" : view === \"offers\" ? \"Offer\" : \"Active\"}</span>}\n priceCell={<PriceText order={order} />}\n actions={\n view === \"received\" ? (\n <>\n <Button size=\"sm\" variant=\"outline\" className=\"h-8 text-xs\" disabled={actions.isProcessing} onClick={() => actions.counter?.(order)}>Counter</Button>\n <Button size=\"sm\" className=\"h-8 text-xs\" disabled={actions.isProcessing} onClick={() => actions.accept?.(order)}>Accept</Button>\n </>\n ) : (\n <Button size=\"sm\" variant=\"outline\" className=\"h-8 text-xs text-destructive hover:text-destructive\" disabled={actions.isProcessing} onClick={() => actions.cancel?.(order)}>Cancel</Button>\n )\n }\n />\n ))}\n </div>\n </div>\n </EmptyOrError>\n {actions.dialogs}\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -51,7 +51,7 @@ const REWARD_ACTION_LABELS = {
|
|
|
51
51
|
create_collection: "Create collections",
|
|
52
52
|
launch_launchpad: "Launch drops / POPs",
|
|
53
53
|
create_remix: "Create remixes",
|
|
54
|
-
list_asset: "
|
|
54
|
+
list_asset: "Sell assets",
|
|
55
55
|
buy_asset: "Buy assets",
|
|
56
56
|
make_offer: "Make offers",
|
|
57
57
|
counter_offer: "Counter offers",
|
|
@@ -109,7 +109,7 @@ const EARN_GROUPS = [
|
|
|
109
109
|
items: [
|
|
110
110
|
{ label: "Collect an asset", href: "/marketplace", Icon: import_lucide_react.ShoppingBag },
|
|
111
111
|
{ label: "Make an offer", href: "/marketplace", Icon: import_lucide_react.Handshake },
|
|
112
|
-
{ label: "
|
|
112
|
+
{ label: "Sell an asset", href: "/portfolio/assets", Icon: import_lucide_react.Tag },
|
|
113
113
|
{ label: "Get an event ticket", href: "/launchpad", Icon: import_lucide_react.Ticket }
|
|
114
114
|
]
|
|
115
115
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/rewards/rewards-dashboard.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"../link.js\";\nimport { Button } from \"../button.js\";\nimport { Skeleton } from \"../skeleton.js\";\nimport { useMedialaneApp } from \"../medialane-app.js\";\nimport { useRewards, useRewardsConfig, useRewardsEvents } from \"../../utils/use-rewards.js\";\nimport { LevelBadge } from \"./level-badge.js\";\nimport { LevelUpCelebration } from \"./level-up-celebration.js\";\nimport { BadgeUnlocked } from \"./badge-unlocked.js\";\nimport { useRewardsCelebrations } from \"./use-rewards-celebrations.js\";\nimport { LeaderboardPanel } from \"./leaderboard-panel.js\";\nimport type { ApiRewardsBadge } from \"@medialane/sdk\";\nimport {\n Gift,\n ExternalLink,\n Palette,\n Layers,\n Tag,\n Handshake,\n ShoppingBag,\n Rocket,\n GitBranch,\n UserRoundCheck,\n Ticket,\n MessageSquare,\n Users,\n type LucideIcon,\n Package,\n CheckCircle2,\n TrendingUp,\n Crown,\n Coins,\n Star,\n Gem,\n Award,\n HandCoins,\n Flame,\n Zap,\n} from \"lucide-react\";\n\nexport const REWARD_ACTION_LABELS: Record<string, string> = {\n complete_profile: \"Complete profile\",\n mint_asset: \"Mint assets\",\n create_collection: \"Create collections\",\n launch_launchpad: \"Launch drops / POPs\",\n create_remix: \"Create remixes\",\n list_asset: \"List assets\",\n buy_asset: \"Buy assets\",\n make_offer: \"Make offers\",\n counter_offer: \"Counter offers\",\n offer_accepted_seller: \"Offers accepted (sold)\",\n offer_accepted_buyer: \"Offers accepted (bought)\",\n claim_pop: \"POP claims\",\n claim_drop: \"Drop claims\",\n comment: \"On-chain comments\",\n create_ticket_collection: \"Ticketed events created\",\n buy_ticket: \"Tickets received\",\n create_club: \"Clubs started\",\n join_club: \"Clubs joined\",\n create_sponsorship_offer: \"Sponsorships opened\",\n place_sponsorship_bid: \"Sponsorship bids\",\n sponsorship_licensed_sponsor: \"Sponsorships secured (sponsor)\",\n sponsorship_licensed_author: \"Sponsorships secured (creator)\",\n launch_coin: \"Creator coins launched\",\n};\n\nconst BADGE_ICONS: Record<string, LucideIcon> = {\n Flame, Package, CheckCircle2, GitBranch, TrendingUp, Layers,\n Ticket, Crown, Coins, Star, Gem, Zap, Award, Users, MessageSquare, HandCoins,\n Handshake,\n};\n\nconst EARN_GROUPS: {\n title: string;\n textClass: string;\n tileClass: string;\n items: { label: string; href: string; Icon: LucideIcon }[];\n}[] = [\n {\n title: \"Create\",\n textClass: \"text-brand-purple\",\n tileClass: \"bg-brand-purple\",\n items: [\n { label: \"Create a collection\", href: \"/launchpad/single-editions/collection\", Icon: Layers },\n { label: \"Mint an asset\", href: \"/launchpad/single-editions\", Icon: Palette },\n { label: \"Launch a drop or POP\", href: \"/launchpad\", Icon: Rocket },\n { label: \"Launch a creator coin\", href: \"/launchpad/coin/create\", Icon: Coins },\n { label: \"Remix existing work\", href: \"/marketplace\", Icon: GitBranch },\n ],\n },\n {\n title: \"Collect\",\n textClass: \"text-brand-blue\",\n tileClass: \"bg-brand-blue\",\n items: [\n { label: \"Collect an asset\", href: \"/marketplace\", Icon: ShoppingBag },\n { label: \"Make an offer\", href: \"/marketplace\", Icon: Handshake },\n { label: \"List an asset for sale\", href: \"/portfolio/assets\", Icon: Tag },\n { label: \"Get an event ticket\", href: \"/launchpad\", Icon: Ticket },\n ],\n },\n {\n title: \"Connect\",\n textClass: \"text-brand-rose\",\n tileClass: \"bg-brand-rose\",\n items: [\n { label: \"Start or join a club\", href: \"/launchpad\", Icon: UserRoundCheck },\n { label: \"Open a sponsorship\", href: \"/launchpad\", Icon: Handshake },\n { label: \"Join the conversation\", href: \"/marketplace\", Icon: MessageSquare },\n { label: \"Complete your profile\", href: \"/settings\", Icon: UserRoundCheck },\n ],\n },\n];\n\nfunction Hero() {\n return (\n <section className=\"space-y-5\">\n <h1 className=\"text-5xl sm:text-6xl font-black tracking-tight leading-[1.02]\">\n Community <span className=\"bg-gradient-to-r from-brand-rose to-brand-orange bg-clip-text text-transparent\">Rewards</span>\n </h1>\n <p className=\"text-lg text-foreground/70 max-w-xl leading-relaxed font-medium\">\n Every action earns XP, and active members receive allocations from the\n Creator's Fund based on how much they participate. Scores and\n badges are recalculated weekly.{\" \"}\n <Link href=\"/journey\" className=\"text-foreground underline underline-offset-2 hover:text-brand-orange transition-colors\">\n See the full system\n </Link>\n .\n </p>\n </section>\n );\n}\n\nfunction SignInButton() {\n const { onRequireWallet, connectLabel } = useMedialaneApp();\n return (\n <Button size=\"lg\" className=\"w-full rounded-full px-7\" onClick={onRequireWallet}>\n {connectLabel ?? \"Sign in\"}\n </Button>\n );\n}\n\nfunction StatusPanel({ address }: { address: string | null | undefined }) {\n const { data: rewards, isLoading } = useRewards(address);\n const { data: config } = useRewardsConfig();\n\n if (!address) {\n return (\n <div className=\"shrink-0 rounded-3xl bg-gradient-to-br from-brand-rose/10 via-brand-orange/[0.06] to-transparent p-6 space-y-3 sm:max-w-xs\">\n <p className=\"text-sm text-foreground/70 font-medium\">Sign in to see your score.</p>\n <SignInButton />\n </div>\n );\n }\n\n if (isLoading || !rewards || !config) {\n return <Skeleton className=\"h-40 w-full sm:w-80 rounded-3xl shrink-0\" />;\n }\n\n const levelXp = config.levels.find((l) => l.level === rewards.currentLevel)?.xpRequired ?? 0;\n const nextLevelXp = rewards.nextLevel?.xpRequired ?? null;\n const hasXp = rewards.totalXp > 0;\n const pct = nextLevelXp ? Math.min(100, Math.round(((rewards.totalXp - levelXp) / (nextLevelXp - levelXp)) * 100)) : 100;\n\n return (\n <div className=\"shrink-0 rounded-3xl bg-gradient-to-br from-brand-rose/10 via-brand-orange/[0.06] to-transparent p-6 sm:min-w-[300px]\">\n <p className=\"text-6xl font-black tabular-nums leading-none tracking-tight\">\n {rewards.totalXp.toLocaleString()}\n </p>\n <p className=\"text-xs font-bold uppercase tracking-widest text-foreground/40 mt-1.5\">XP earned</p>\n\n <div className=\"mt-4 flex items-center gap-2\">\n <LevelBadge level={rewards.currentLevel} name={rewards.currentLevelName} badgeColor={rewards.badgeColor} size=\"md\" />\n </div>\n {!hasXp && (\n <p className=\"text-sm text-foreground/60 font-medium mt-2\">Mint, collect, or complete your profile to start.</p>\n )}\n {rewards.nextLevel && (\n <div className=\"mt-3 space-y-1.5\">\n <div className=\"h-2 w-full rounded-full bg-foreground/[0.08] overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-gradient-to-r from-brand-rose to-brand-orange transition-[width]\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <p className=\"text-sm font-semibold text-foreground/60\">\n {(nextLevelXp! - rewards.totalXp).toLocaleString()} XP to {rewards.nextLevel.name}\n </p>\n </div>\n )}\n </div>\n );\n}\n\nfunction PersonalPanel({ address }: { address: string | null | undefined }) {\n const { data: rewards } = useRewards(address);\n const { data: events } = useRewardsEvents(address, 1, 6);\n const { data: config } = useRewardsConfig();\n\n const actionLabel = (type: string) =>\n config?.actions.find((a) => a.type === type)?.label ?? REWARD_ACTION_LABELS[type] ?? type;\n\n if (!address || !rewards || Object.keys(rewards.breakdown).length === 0) return null;\n\n return (\n <section className=\"space-y-5\">\n <SectionTitle>Your breakdown</SectionTitle>\n <div className=\"divide-y divide-foreground/[0.06]\">\n {Object.entries(rewards.breakdown)\n .sort(([, a], [, b]) => b - a)\n .map(([action, xp]) => (\n <div key={action} className=\"flex items-center justify-between py-3.5\">\n <span className=\"text-base font-medium text-foreground/70\">{actionLabel(action)}</span>\n <span className=\"text-lg font-black tabular-nums\">+{xp.toLocaleString()}</span>\n </div>\n ))}\n </div>\n {events && events.data.length > 0 && (\n <div className=\"divide-y divide-foreground/[0.06]\">\n {events.data.map((e) => (\n <div key={e.id} className=\"flex items-center justify-between py-3.5\">\n <span className=\"text-base font-medium text-foreground/70\">{actionLabel(e.actionType)}</span>\n <span className=\"text-lg font-black tabular-nums text-brand-rose\">+{e.finalXp}</span>\n </div>\n ))}\n </div>\n )}\n </section>\n );\n}\n\nfunction CommunityPanel({ address }: { address: string | null | undefined }) {\n return (\n <section className=\"space-y-5\">\n <div>\n <SectionTitle>Community</SectionTitle>\n <p className=\"text-base text-foreground/60 mt-1.5 font-medium\">Real members already earning XP.</p>\n </div>\n <LeaderboardPanel myAddress={address} showHeading={false} limit={12} />\n </section>\n );\n}\n\nfunction EarnMorePanel() {\n return (\n <section className=\"space-y-6\">\n <SectionTitle>Ways to earn</SectionTitle>\n {EARN_GROUPS.map((group) => (\n <div key={group.title} className=\"space-y-3\">\n <p className={`text-sm font-black uppercase tracking-wide ${group.textClass}`}>{group.title}</p>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3\">\n {group.items.map(({ label, href, Icon }) => (\n <Link\n key={href + label}\n href={href}\n className=\"group flex flex-col gap-3 rounded-2xl bg-foreground/[0.04] hover:bg-foreground/[0.07] p-4 transition-colors\"\n >\n <span className={`flex h-10 w-10 items-center justify-center rounded-xl text-white shrink-0 ${group.tileClass}`}>\n <Icon className=\"h-5 w-5\" />\n </span>\n <span className=\"text-sm font-bold leading-snug text-foreground\">{label}</span>\n </Link>\n ))}\n </div>\n </div>\n ))}\n </section>\n );\n}\n\nfunction CreatorsFundCard() {\n return (\n <section className=\"relative overflow-hidden rounded-3xl bg-gradient-to-br from-brand-rose to-brand-orange p-8 text-white\">\n <div className=\"pointer-events-none absolute -top-16 -right-16 h-56 w-56 rounded-full bg-white/10 blur-3xl\" />\n <div className=\"pointer-events-none absolute -bottom-20 -left-10 h-56 w-56 rounded-full bg-black/10 blur-3xl\" />\n\n <div className=\"relative space-y-6\">\n <h2 className=\"text-3xl font-black tracking-tight leading-none\">\n Creator's Fund\n </h2>\n\n <p className=\"text-base font-medium text-white/90 leading-relaxed max-w-sm\">\n A share of what Medialane earns is set aside in one public wallet and airdropped back\n to the people creating and trading here, split by XP.\n </p>\n\n <div className=\"flex flex-wrap items-center gap-4 pt-2\">\n <Link\n href=\"/airdrop\"\n className=\"inline-flex items-center gap-2 rounded-full bg-white px-6 py-3 text-sm font-black text-brand-rose hover:bg-white/90 transition-colors\"\n >\n <Gift className=\"h-4 w-4\" />\n How the fund works\n </Link>\n <a\n href=\"https://medialane.org/creators-fund\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-1.5 text-sm font-bold text-white/90 hover:text-white transition-colors\"\n >\n Watch the wallet\n <ExternalLink className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n </div>\n </section>\n );\n}\n\nfunction BadgesPanel({ address }: { address: string | null | undefined }) {\n const { data: config, isLoading } = useRewardsConfig();\n const { data: rewards } = useRewards(address);\n\n if (isLoading) {\n return (\n <div className=\"grid grid-cols-3 gap-3\">\n {Array.from({ length: 6 }).map((_, i) => (\n <Skeleton key={i} className=\"h-16 rounded-2xl\" />\n ))}\n </div>\n );\n }\n\n if (!config) return null;\n\n const earnedKeys = new Set(rewards?.badges.map((b) => b.key) ?? []);\n\n return (\n <section className=\"space-y-4\">\n <SectionTitle>Badges to earn</SectionTitle>\n <div className=\"grid grid-cols-3 gap-3\">\n {config.badges.map((badge: ApiRewardsBadge) => {\n const isEarned = earnedKeys.has(badge.key);\n const Icon = BADGE_ICONS[badge.icon] ?? Award;\n return (\n <div\n key={badge.key}\n title={badge.description}\n className={`flex flex-col items-center gap-1.5 rounded-2xl p-3 text-center transition-colors ${\n isEarned ? \"bg-brand-blue text-white\" : \"bg-foreground/[0.04] text-foreground/40\"\n }`}\n >\n <Icon className=\"h-5 w-5\" />\n <span className=\"text-xs font-bold leading-tight\">{badge.name}</span>\n </div>\n );\n })}\n </div>\n </section>\n );\n}\n\nfunction SectionTitle({ children }: { children: React.ReactNode }) {\n return <h2 className=\"text-2xl font-black tracking-tight text-foreground\">{children}</h2>;\n}\n\nexport function RewardsDashboard() {\n const { walletAddress: address } = useMedialaneApp();\n const { data: rewards } = useRewards(address);\n const { leveledUpTo, newBadgeKeys, dismiss } = useRewardsCelebrations(address, rewards ?? null);\n\n const unlockedBadges = rewards\n ? newBadgeKeys.map((key) => rewards.badges.find((b) => b.key === key)).filter((b) => b !== undefined)\n : [];\n\n return (\n <div className=\"space-y-14\">\n {unlockedBadges.length > 0 && (\n <div className=\"space-y-3\">\n {unlockedBadges.map((badge) => (\n <BadgeUnlocked key={badge.key} badge={badge} />\n ))}\n </div>\n )}\n\n {leveledUpTo !== null && rewards && (\n <LevelUpCelebration\n level={leveledUpTo}\n name={rewards.currentLevelName}\n badgeColor={rewards.badgeColor}\n onDismiss={dismiss}\n />\n )}\n\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-8\">\n <Hero />\n <StatusPanel address={address} />\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-12 gap-14 items-start\">\n\n <div className=\"lg:col-span-7 space-y-14\">\n <CommunityPanel address={address} />\n <PersonalPanel address={address} />\n </div>\n\n <div className=\"lg:col-span-5 space-y-10\">\n <EarnMorePanel />\n <CreatorsFundCard />\n <BadgesPanel address={address} />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["Link"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/rewards/rewards-dashboard.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"../link.js\";\nimport { Button } from \"../button.js\";\nimport { Skeleton } from \"../skeleton.js\";\nimport { useMedialaneApp } from \"../medialane-app.js\";\nimport { useRewards, useRewardsConfig, useRewardsEvents } from \"../../utils/use-rewards.js\";\nimport { LevelBadge } from \"./level-badge.js\";\nimport { LevelUpCelebration } from \"./level-up-celebration.js\";\nimport { BadgeUnlocked } from \"./badge-unlocked.js\";\nimport { useRewardsCelebrations } from \"./use-rewards-celebrations.js\";\nimport { LeaderboardPanel } from \"./leaderboard-panel.js\";\nimport type { ApiRewardsBadge } from \"@medialane/sdk\";\nimport {\n Gift,\n ExternalLink,\n Palette,\n Layers,\n Tag,\n Handshake,\n ShoppingBag,\n Rocket,\n GitBranch,\n UserRoundCheck,\n Ticket,\n MessageSquare,\n Users,\n type LucideIcon,\n Package,\n CheckCircle2,\n TrendingUp,\n Crown,\n Coins,\n Star,\n Gem,\n Award,\n HandCoins,\n Flame,\n Zap,\n} from \"lucide-react\";\n\nexport const REWARD_ACTION_LABELS: Record<string, string> = {\n complete_profile: \"Complete profile\",\n mint_asset: \"Mint assets\",\n create_collection: \"Create collections\",\n launch_launchpad: \"Launch drops / POPs\",\n create_remix: \"Create remixes\",\n list_asset: \"Sell assets\",\n buy_asset: \"Buy assets\",\n make_offer: \"Make offers\",\n counter_offer: \"Counter offers\",\n offer_accepted_seller: \"Offers accepted (sold)\",\n offer_accepted_buyer: \"Offers accepted (bought)\",\n claim_pop: \"POP claims\",\n claim_drop: \"Drop claims\",\n comment: \"On-chain comments\",\n create_ticket_collection: \"Ticketed events created\",\n buy_ticket: \"Tickets received\",\n create_club: \"Clubs started\",\n join_club: \"Clubs joined\",\n create_sponsorship_offer: \"Sponsorships opened\",\n place_sponsorship_bid: \"Sponsorship bids\",\n sponsorship_licensed_sponsor: \"Sponsorships secured (sponsor)\",\n sponsorship_licensed_author: \"Sponsorships secured (creator)\",\n launch_coin: \"Creator coins launched\",\n};\n\nconst BADGE_ICONS: Record<string, LucideIcon> = {\n Flame, Package, CheckCircle2, GitBranch, TrendingUp, Layers,\n Ticket, Crown, Coins, Star, Gem, Zap, Award, Users, MessageSquare, HandCoins,\n Handshake,\n};\n\nconst EARN_GROUPS: {\n title: string;\n textClass: string;\n tileClass: string;\n items: { label: string; href: string; Icon: LucideIcon }[];\n}[] = [\n {\n title: \"Create\",\n textClass: \"text-brand-purple\",\n tileClass: \"bg-brand-purple\",\n items: [\n { label: \"Create a collection\", href: \"/launchpad/single-editions/collection\", Icon: Layers },\n { label: \"Mint an asset\", href: \"/launchpad/single-editions\", Icon: Palette },\n { label: \"Launch a drop or POP\", href: \"/launchpad\", Icon: Rocket },\n { label: \"Launch a creator coin\", href: \"/launchpad/coin/create\", Icon: Coins },\n { label: \"Remix existing work\", href: \"/marketplace\", Icon: GitBranch },\n ],\n },\n {\n title: \"Collect\",\n textClass: \"text-brand-blue\",\n tileClass: \"bg-brand-blue\",\n items: [\n { label: \"Collect an asset\", href: \"/marketplace\", Icon: ShoppingBag },\n { label: \"Make an offer\", href: \"/marketplace\", Icon: Handshake },\n { label: \"Sell an asset\", href: \"/portfolio/assets\", Icon: Tag },\n { label: \"Get an event ticket\", href: \"/launchpad\", Icon: Ticket },\n ],\n },\n {\n title: \"Connect\",\n textClass: \"text-brand-rose\",\n tileClass: \"bg-brand-rose\",\n items: [\n { label: \"Start or join a club\", href: \"/launchpad\", Icon: UserRoundCheck },\n { label: \"Open a sponsorship\", href: \"/launchpad\", Icon: Handshake },\n { label: \"Join the conversation\", href: \"/marketplace\", Icon: MessageSquare },\n { label: \"Complete your profile\", href: \"/settings\", Icon: UserRoundCheck },\n ],\n },\n];\n\nfunction Hero() {\n return (\n <section className=\"space-y-5\">\n <h1 className=\"text-5xl sm:text-6xl font-black tracking-tight leading-[1.02]\">\n Community <span className=\"bg-gradient-to-r from-brand-rose to-brand-orange bg-clip-text text-transparent\">Rewards</span>\n </h1>\n <p className=\"text-lg text-foreground/70 max-w-xl leading-relaxed font-medium\">\n Every action earns XP, and active members receive allocations from the\n Creator's Fund based on how much they participate. Scores and\n badges are recalculated weekly.{\" \"}\n <Link href=\"/journey\" className=\"text-foreground underline underline-offset-2 hover:text-brand-orange transition-colors\">\n See the full system\n </Link>\n .\n </p>\n </section>\n );\n}\n\nfunction SignInButton() {\n const { onRequireWallet, connectLabel } = useMedialaneApp();\n return (\n <Button size=\"lg\" className=\"w-full rounded-full px-7\" onClick={onRequireWallet}>\n {connectLabel ?? \"Sign in\"}\n </Button>\n );\n}\n\nfunction StatusPanel({ address }: { address: string | null | undefined }) {\n const { data: rewards, isLoading } = useRewards(address);\n const { data: config } = useRewardsConfig();\n\n if (!address) {\n return (\n <div className=\"shrink-0 rounded-3xl bg-gradient-to-br from-brand-rose/10 via-brand-orange/[0.06] to-transparent p-6 space-y-3 sm:max-w-xs\">\n <p className=\"text-sm text-foreground/70 font-medium\">Sign in to see your score.</p>\n <SignInButton />\n </div>\n );\n }\n\n if (isLoading || !rewards || !config) {\n return <Skeleton className=\"h-40 w-full sm:w-80 rounded-3xl shrink-0\" />;\n }\n\n const levelXp = config.levels.find((l) => l.level === rewards.currentLevel)?.xpRequired ?? 0;\n const nextLevelXp = rewards.nextLevel?.xpRequired ?? null;\n const hasXp = rewards.totalXp > 0;\n const pct = nextLevelXp ? Math.min(100, Math.round(((rewards.totalXp - levelXp) / (nextLevelXp - levelXp)) * 100)) : 100;\n\n return (\n <div className=\"shrink-0 rounded-3xl bg-gradient-to-br from-brand-rose/10 via-brand-orange/[0.06] to-transparent p-6 sm:min-w-[300px]\">\n <p className=\"text-6xl font-black tabular-nums leading-none tracking-tight\">\n {rewards.totalXp.toLocaleString()}\n </p>\n <p className=\"text-xs font-bold uppercase tracking-widest text-foreground/40 mt-1.5\">XP earned</p>\n\n <div className=\"mt-4 flex items-center gap-2\">\n <LevelBadge level={rewards.currentLevel} name={rewards.currentLevelName} badgeColor={rewards.badgeColor} size=\"md\" />\n </div>\n {!hasXp && (\n <p className=\"text-sm text-foreground/60 font-medium mt-2\">Mint, collect, or complete your profile to start.</p>\n )}\n {rewards.nextLevel && (\n <div className=\"mt-3 space-y-1.5\">\n <div className=\"h-2 w-full rounded-full bg-foreground/[0.08] overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-gradient-to-r from-brand-rose to-brand-orange transition-[width]\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <p className=\"text-sm font-semibold text-foreground/60\">\n {(nextLevelXp! - rewards.totalXp).toLocaleString()} XP to {rewards.nextLevel.name}\n </p>\n </div>\n )}\n </div>\n );\n}\n\nfunction PersonalPanel({ address }: { address: string | null | undefined }) {\n const { data: rewards } = useRewards(address);\n const { data: events } = useRewardsEvents(address, 1, 6);\n const { data: config } = useRewardsConfig();\n\n const actionLabel = (type: string) =>\n config?.actions.find((a) => a.type === type)?.label ?? REWARD_ACTION_LABELS[type] ?? type;\n\n if (!address || !rewards || Object.keys(rewards.breakdown).length === 0) return null;\n\n return (\n <section className=\"space-y-5\">\n <SectionTitle>Your breakdown</SectionTitle>\n <div className=\"divide-y divide-foreground/[0.06]\">\n {Object.entries(rewards.breakdown)\n .sort(([, a], [, b]) => b - a)\n .map(([action, xp]) => (\n <div key={action} className=\"flex items-center justify-between py-3.5\">\n <span className=\"text-base font-medium text-foreground/70\">{actionLabel(action)}</span>\n <span className=\"text-lg font-black tabular-nums\">+{xp.toLocaleString()}</span>\n </div>\n ))}\n </div>\n {events && events.data.length > 0 && (\n <div className=\"divide-y divide-foreground/[0.06]\">\n {events.data.map((e) => (\n <div key={e.id} className=\"flex items-center justify-between py-3.5\">\n <span className=\"text-base font-medium text-foreground/70\">{actionLabel(e.actionType)}</span>\n <span className=\"text-lg font-black tabular-nums text-brand-rose\">+{e.finalXp}</span>\n </div>\n ))}\n </div>\n )}\n </section>\n );\n}\n\nfunction CommunityPanel({ address }: { address: string | null | undefined }) {\n return (\n <section className=\"space-y-5\">\n <div>\n <SectionTitle>Community</SectionTitle>\n <p className=\"text-base text-foreground/60 mt-1.5 font-medium\">Real members already earning XP.</p>\n </div>\n <LeaderboardPanel myAddress={address} showHeading={false} limit={12} />\n </section>\n );\n}\n\nfunction EarnMorePanel() {\n return (\n <section className=\"space-y-6\">\n <SectionTitle>Ways to earn</SectionTitle>\n {EARN_GROUPS.map((group) => (\n <div key={group.title} className=\"space-y-3\">\n <p className={`text-sm font-black uppercase tracking-wide ${group.textClass}`}>{group.title}</p>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3\">\n {group.items.map(({ label, href, Icon }) => (\n <Link\n key={href + label}\n href={href}\n className=\"group flex flex-col gap-3 rounded-2xl bg-foreground/[0.04] hover:bg-foreground/[0.07] p-4 transition-colors\"\n >\n <span className={`flex h-10 w-10 items-center justify-center rounded-xl text-white shrink-0 ${group.tileClass}`}>\n <Icon className=\"h-5 w-5\" />\n </span>\n <span className=\"text-sm font-bold leading-snug text-foreground\">{label}</span>\n </Link>\n ))}\n </div>\n </div>\n ))}\n </section>\n );\n}\n\nfunction CreatorsFundCard() {\n return (\n <section className=\"relative overflow-hidden rounded-3xl bg-gradient-to-br from-brand-rose to-brand-orange p-8 text-white\">\n <div className=\"pointer-events-none absolute -top-16 -right-16 h-56 w-56 rounded-full bg-white/10 blur-3xl\" />\n <div className=\"pointer-events-none absolute -bottom-20 -left-10 h-56 w-56 rounded-full bg-black/10 blur-3xl\" />\n\n <div className=\"relative space-y-6\">\n <h2 className=\"text-3xl font-black tracking-tight leading-none\">\n Creator's Fund\n </h2>\n\n <p className=\"text-base font-medium text-white/90 leading-relaxed max-w-sm\">\n A share of what Medialane earns is set aside in one public wallet and airdropped back\n to the people creating and trading here, split by XP.\n </p>\n\n <div className=\"flex flex-wrap items-center gap-4 pt-2\">\n <Link\n href=\"/airdrop\"\n className=\"inline-flex items-center gap-2 rounded-full bg-white px-6 py-3 text-sm font-black text-brand-rose hover:bg-white/90 transition-colors\"\n >\n <Gift className=\"h-4 w-4\" />\n How the fund works\n </Link>\n <a\n href=\"https://medialane.org/creators-fund\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-1.5 text-sm font-bold text-white/90 hover:text-white transition-colors\"\n >\n Watch the wallet\n <ExternalLink className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n </div>\n </section>\n );\n}\n\nfunction BadgesPanel({ address }: { address: string | null | undefined }) {\n const { data: config, isLoading } = useRewardsConfig();\n const { data: rewards } = useRewards(address);\n\n if (isLoading) {\n return (\n <div className=\"grid grid-cols-3 gap-3\">\n {Array.from({ length: 6 }).map((_, i) => (\n <Skeleton key={i} className=\"h-16 rounded-2xl\" />\n ))}\n </div>\n );\n }\n\n if (!config) return null;\n\n const earnedKeys = new Set(rewards?.badges.map((b) => b.key) ?? []);\n\n return (\n <section className=\"space-y-4\">\n <SectionTitle>Badges to earn</SectionTitle>\n <div className=\"grid grid-cols-3 gap-3\">\n {config.badges.map((badge: ApiRewardsBadge) => {\n const isEarned = earnedKeys.has(badge.key);\n const Icon = BADGE_ICONS[badge.icon] ?? Award;\n return (\n <div\n key={badge.key}\n title={badge.description}\n className={`flex flex-col items-center gap-1.5 rounded-2xl p-3 text-center transition-colors ${\n isEarned ? \"bg-brand-blue text-white\" : \"bg-foreground/[0.04] text-foreground/40\"\n }`}\n >\n <Icon className=\"h-5 w-5\" />\n <span className=\"text-xs font-bold leading-tight\">{badge.name}</span>\n </div>\n );\n })}\n </div>\n </section>\n );\n}\n\nfunction SectionTitle({ children }: { children: React.ReactNode }) {\n return <h2 className=\"text-2xl font-black tracking-tight text-foreground\">{children}</h2>;\n}\n\nexport function RewardsDashboard() {\n const { walletAddress: address } = useMedialaneApp();\n const { data: rewards } = useRewards(address);\n const { leveledUpTo, newBadgeKeys, dismiss } = useRewardsCelebrations(address, rewards ?? null);\n\n const unlockedBadges = rewards\n ? newBadgeKeys.map((key) => rewards.badges.find((b) => b.key === key)).filter((b) => b !== undefined)\n : [];\n\n return (\n <div className=\"space-y-14\">\n {unlockedBadges.length > 0 && (\n <div className=\"space-y-3\">\n {unlockedBadges.map((badge) => (\n <BadgeUnlocked key={badge.key} badge={badge} />\n ))}\n </div>\n )}\n\n {leveledUpTo !== null && rewards && (\n <LevelUpCelebration\n level={leveledUpTo}\n name={rewards.currentLevelName}\n badgeColor={rewards.badgeColor}\n onDismiss={dismiss}\n />\n )}\n\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-8\">\n <Hero />\n <StatusPanel address={address} />\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-12 gap-14 items-start\">\n\n <div className=\"lg:col-span-7 space-y-14\">\n <CommunityPanel address={address} />\n <PersonalPanel address={address} />\n </div>\n\n <div className=\"lg:col-span-5 space-y-10\">\n <EarnMorePanel />\n <CreatorsFundCard />\n <BadgesPanel address={address} />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["Link"]}
|
|
@@ -42,7 +42,7 @@ const REWARD_ACTION_LABELS = {
|
|
|
42
42
|
create_collection: "Create collections",
|
|
43
43
|
launch_launchpad: "Launch drops / POPs",
|
|
44
44
|
create_remix: "Create remixes",
|
|
45
|
-
list_asset: "
|
|
45
|
+
list_asset: "Sell assets",
|
|
46
46
|
buy_asset: "Buy assets",
|
|
47
47
|
make_offer: "Make offers",
|
|
48
48
|
counter_offer: "Counter offers",
|
|
@@ -100,7 +100,7 @@ const EARN_GROUPS = [
|
|
|
100
100
|
items: [
|
|
101
101
|
{ label: "Collect an asset", href: "/marketplace", Icon: ShoppingBag },
|
|
102
102
|
{ label: "Make an offer", href: "/marketplace", Icon: Handshake },
|
|
103
|
-
{ label: "
|
|
103
|
+
{ label: "Sell an asset", href: "/portfolio/assets", Icon: Tag },
|
|
104
104
|
{ label: "Get an event ticket", href: "/launchpad", Icon: Ticket }
|
|
105
105
|
]
|
|
106
106
|
},
|