@medialane/ui 0.105.0 → 0.107.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/asset-lightbox.cjs +66 -0
- package/dist/components/asset-lightbox.cjs.map +1 -0
- package/dist/components/asset-lightbox.d.cts +11 -0
- package/dist/components/asset-lightbox.d.ts +11 -0
- package/dist/components/asset-lightbox.js +42 -0
- package/dist/components/asset-lightbox.js.map +1 -0
- package/dist/components/club-owner-actions.cjs +56 -0
- package/dist/components/club-owner-actions.cjs.map +1 -0
- package/dist/components/club-owner-actions.d.cts +11 -0
- package/dist/components/club-owner-actions.d.ts +11 -0
- package/dist/components/club-owner-actions.js +22 -0
- package/dist/components/club-owner-actions.js.map +1 -0
- package/dist/components/collection-activity-tab.cjs +60 -0
- package/dist/components/collection-activity-tab.cjs.map +1 -0
- package/dist/components/collection-activity-tab.d.cts +10 -0
- package/dist/components/collection-activity-tab.d.ts +10 -0
- package/dist/components/collection-activity-tab.js +36 -0
- package/dist/components/collection-activity-tab.js.map +1 -0
- package/dist/components/collection-traits-tab.cjs +79 -0
- package/dist/components/collection-traits-tab.cjs.map +1 -0
- package/dist/components/collection-traits-tab.d.cts +10 -0
- package/dist/components/collection-traits-tab.d.ts +10 -0
- package/dist/components/collection-traits-tab.js +55 -0
- package/dist/components/collection-traits-tab.js.map +1 -0
- package/dist/components/create-form-primitives.cjs +61 -0
- package/dist/components/create-form-primitives.cjs.map +1 -0
- package/dist/components/create-form-primitives.d.cts +16 -0
- package/dist/components/create-form-primitives.d.ts +16 -0
- package/dist/components/create-form-primitives.js +36 -0
- package/dist/components/create-form-primitives.js.map +1 -0
- package/dist/components/creation-record.cjs +86 -0
- package/dist/components/creation-record.cjs.map +1 -0
- package/dist/components/creation-record.d.cts +14 -0
- package/dist/components/creation-record.d.ts +14 -0
- package/dist/components/creation-record.js +52 -0
- package/dist/components/creation-record.js.map +1 -0
- package/dist/components/creator-chip.cjs +58 -0
- package/dist/components/creator-chip.cjs.map +1 -0
- package/dist/components/creator-chip.d.cts +10 -0
- package/dist/components/creator-chip.d.ts +10 -0
- package/dist/components/creator-chip.js +24 -0
- package/dist/components/creator-chip.js.map +1 -0
- package/dist/components/creator-score-inline.cjs +52 -0
- package/dist/components/creator-score-inline.cjs.map +1 -0
- package/dist/components/creator-score-inline.d.cts +19 -0
- package/dist/components/creator-score-inline.d.ts +19 -0
- package/dist/components/creator-score-inline.js +18 -0
- package/dist/components/creator-score-inline.js.map +1 -0
- package/dist/components/dialog.d.cts +9 -9
- package/dist/components/dialog.d.ts +9 -9
- package/dist/components/ip-type-fields.cjs +307 -0
- package/dist/components/ip-type-fields.cjs.map +1 -0
- package/dist/components/ip-type-fields.d.cts +17 -0
- package/dist/components/ip-type-fields.d.ts +17 -0
- package/dist/components/ip-type-fields.js +293 -0
- package/dist/components/ip-type-fields.js.map +1 -0
- package/dist/components/json-ld.cjs +39 -0
- package/dist/components/json-ld.cjs.map +1 -0
- package/dist/components/json-ld.d.cts +8 -0
- package/dist/components/json-ld.d.ts +8 -0
- package/dist/components/json-ld.js +15 -0
- package/dist/components/json-ld.js.map +1 -0
- package/dist/components/nav-theme-toggle.cjs +66 -0
- package/dist/components/nav-theme-toggle.cjs.map +1 -0
- package/dist/components/nav-theme-toggle.d.cts +9 -0
- package/dist/components/nav-theme-toggle.d.ts +9 -0
- package/dist/components/nav-theme-toggle.js +42 -0
- package/dist/components/nav-theme-toggle.js.map +1 -0
- package/dist/components/order-sort-control.cjs +58 -0
- package/dist/components/order-sort-control.cjs.map +1 -0
- package/dist/components/order-sort-control.d.cts +13 -0
- package/dist/components/order-sort-control.d.ts +13 -0
- package/dist/components/order-sort-control.js +39 -0
- package/dist/components/order-sort-control.js.map +1 -0
- package/dist/components/portfolio-activity.cjs +117 -0
- package/dist/components/portfolio-activity.cjs.map +1 -0
- package/dist/components/portfolio-activity.d.cts +10 -0
- package/dist/components/portfolio-activity.d.ts +10 -0
- package/dist/components/portfolio-activity.js +93 -0
- package/dist/components/portfolio-activity.js.map +1 -0
- package/dist/components/price-history-chart.cjs +130 -0
- package/dist/components/price-history-chart.cjs.map +1 -0
- package/dist/components/price-history-chart.d.cts +9 -0
- package/dist/components/price-history-chart.d.ts +9 -0
- package/dist/components/price-history-chart.js +114 -0
- package/dist/components/price-history-chart.js.map +1 -0
- package/dist/components/sheet.cjs +160 -0
- package/dist/components/sheet.cjs.map +1 -0
- package/dist/components/sheet.d.cts +30 -0
- package/dist/components/sheet.d.ts +30 -0
- package/dist/components/sheet.js +117 -0
- package/dist/components/sheet.js.map +1 -0
- package/dist/components/textarea.cjs +57 -0
- package/dist/components/textarea.cjs.map +1 -0
- package/dist/components/textarea.d.cts +6 -0
- package/dist/components/textarea.d.ts +6 -0
- package/dist/components/textarea.js +23 -0
- package/dist/components/textarea.js.map +1 -0
- package/dist/index.cjs +110 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -2
- package/dist/index.d.ts +24 -2
- package/dist/index.js +88 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/format-activity.cjs +143 -0
- package/dist/utils/format-activity.cjs.map +1 -0
- package/dist/utils/format-activity.d.cts +14 -0
- package/dist/utils/format-activity.d.ts +14 -0
- package/dist/utils/format-activity.js +116 -0
- package/dist/utils/format-activity.js.map +1 -0
- package/dist/utils/proxy-body.cjs +60 -0
- package/dist/utils/proxy-body.cjs.map +1 -0
- package/dist/utils/proxy-body.d.cts +22 -0
- package/dist/utils/proxy-body.d.ts +22 -0
- package/dist/utils/proxy-body.js +36 -0
- package/dist/utils/proxy-body.js.map +1 -0
- package/dist/utils/query-keys.cjs +80 -0
- package/dist/utils/query-keys.cjs.map +1 -0
- package/dist/utils/query-keys.d.cts +39 -0
- package/dist/utils/query-keys.d.ts +39 -0
- package/dist/utils/query-keys.js +54 -0
- package/dist/utils/query-keys.js.map +1 -0
- package/dist/utils/use-activities.cjs +68 -0
- package/dist/utils/use-activities.cjs.map +1 -0
- package/dist/utils/use-activities.d.cts +20 -0
- package/dist/utils/use-activities.d.ts +20 -0
- package/dist/utils/use-activities.js +33 -0
- package/dist/utils/use-activities.js.map +1 -0
- package/dist/utils/use-collections.cjs +113 -0
- package/dist/utils/use-collections.cjs.map +1 -0
- package/dist/utils/use-collections.d.cts +43 -0
- package/dist/utils/use-collections.d.ts +43 -0
- package/dist/utils/use-collections.js +75 -0
- package/dist/utils/use-collections.js.map +1 -0
- package/dist/utils/use-profiles.cjs +69 -0
- package/dist/utils/use-profiles.cjs.map +1 -0
- package/dist/utils/use-profiles.d.cts +18 -0
- package/dist/utils/use-profiles.d.ts +18 -0
- package/dist/utils/use-profiles.js +34 -0
- package/dist/utils/use-profiles.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ApiOrder } from '@medialane/sdk';
|
|
3
|
+
|
|
4
|
+
type OrderSort = "recent" | "price_asc" | "price_desc";
|
|
5
|
+
/** Pure — orders already come from the API sorted "recent"; this only
|
|
6
|
+
* re-sorts the in-memory page by price.raw (BigInt-safe wei comparison). */
|
|
7
|
+
declare function sortOrders(orders: ApiOrder[], sort: OrderSort): ApiOrder[];
|
|
8
|
+
declare function OrderSortControl({ value, onChange }: {
|
|
9
|
+
value: OrderSort;
|
|
10
|
+
onChange: (v: OrderSort) => void;
|
|
11
|
+
}): react_jsx_runtime.JSX.Element;
|
|
12
|
+
|
|
13
|
+
export { type OrderSort, OrderSortControl, sortOrders };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ApiOrder } from '@medialane/sdk';
|
|
3
|
+
|
|
4
|
+
type OrderSort = "recent" | "price_asc" | "price_desc";
|
|
5
|
+
/** Pure — orders already come from the API sorted "recent"; this only
|
|
6
|
+
* re-sorts the in-memory page by price.raw (BigInt-safe wei comparison). */
|
|
7
|
+
declare function sortOrders(orders: ApiOrder[], sort: OrderSort): ApiOrder[];
|
|
8
|
+
declare function OrderSortControl({ value, onChange }: {
|
|
9
|
+
value: OrderSort;
|
|
10
|
+
onChange: (v: OrderSort) => void;
|
|
11
|
+
}): react_jsx_runtime.JSX.Element;
|
|
12
|
+
|
|
13
|
+
export { type OrderSort, OrderSortControl, sortOrders };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ArrowDownUp } from "lucide-react";
|
|
4
|
+
import {
|
|
5
|
+
Select,
|
|
6
|
+
SelectContent,
|
|
7
|
+
SelectItem,
|
|
8
|
+
SelectTrigger,
|
|
9
|
+
SelectValue
|
|
10
|
+
} from "./select.js";
|
|
11
|
+
const LABELS = {
|
|
12
|
+
recent: "Recently listed",
|
|
13
|
+
price_asc: "Price: low to high",
|
|
14
|
+
price_desc: "Price: high to low"
|
|
15
|
+
};
|
|
16
|
+
function sortOrders(orders, sort) {
|
|
17
|
+
if (sort === "recent") return orders;
|
|
18
|
+
const withPrice = orders.filter((o) => o.price.raw != null);
|
|
19
|
+
const withoutPrice = orders.filter((o) => o.price.raw == null);
|
|
20
|
+
const sorted = [...withPrice].sort((a, b) => {
|
|
21
|
+
const diff = BigInt(a.price.raw) - BigInt(b.price.raw);
|
|
22
|
+
return sort === "price_asc" ? diff < 0n ? -1 : diff > 0n ? 1 : 0 : diff > 0n ? -1 : diff < 0n ? 1 : 0;
|
|
23
|
+
});
|
|
24
|
+
return [...sorted, ...withoutPrice];
|
|
25
|
+
}
|
|
26
|
+
function OrderSortControl({ value, onChange }) {
|
|
27
|
+
return /* @__PURE__ */ jsxs(Select, { value, onValueChange: (v) => onChange(v), children: [
|
|
28
|
+
/* @__PURE__ */ jsxs(SelectTrigger, { className: "w-[180px] h-9 text-xs gap-1.5", children: [
|
|
29
|
+
/* @__PURE__ */ jsx(ArrowDownUp, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
30
|
+
/* @__PURE__ */ jsx(SelectValue, {})
|
|
31
|
+
] }),
|
|
32
|
+
/* @__PURE__ */ jsx(SelectContent, { children: Object.keys(LABELS).map((k) => /* @__PURE__ */ jsx(SelectItem, { value: k, className: "text-xs", children: LABELS[k] }, k)) })
|
|
33
|
+
] });
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
OrderSortControl,
|
|
37
|
+
sortOrders
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=order-sort-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/order-sort-control.tsx"],"sourcesContent":["\"use client\";\n\nimport { ArrowDownUp } from \"lucide-react\";\nimport type { ApiOrder } from \"@medialane/sdk\";\nimport {\n Select, SelectContent, SelectItem, SelectTrigger, SelectValue,\n} from \"./select.js\";\n\nexport type OrderSort = \"recent\" | \"price_asc\" | \"price_desc\";\n\nconst LABELS: Record<OrderSort, string> = {\n recent: \"Recently listed\",\n price_asc: \"Price: low to high\",\n price_desc: \"Price: high to low\",\n};\n\n/** Pure — orders already come from the API sorted \"recent\"; this only\n * re-sorts the in-memory page by price.raw (BigInt-safe wei comparison). */\nexport function sortOrders(orders: ApiOrder[], sort: OrderSort): ApiOrder[] {\n if (sort === \"recent\") return orders;\n const withPrice = orders.filter((o) => o.price.raw != null);\n const withoutPrice = orders.filter((o) => o.price.raw == null);\n const sorted = [...withPrice].sort((a, b) => {\n const diff = BigInt(a.price.raw!) - BigInt(b.price.raw!);\n return sort === \"price_asc\" ? (diff < 0n ? -1 : diff > 0n ? 1 : 0) : (diff > 0n ? -1 : diff < 0n ? 1 : 0);\n });\n return [...sorted, ...withoutPrice];\n}\n\nexport function OrderSortControl({ value, onChange }: { value: OrderSort; onChange: (v: OrderSort) => void }) {\n return (\n <Select value={value} onValueChange={(v) => onChange(v as OrderSort)}>\n <SelectTrigger className=\"w-[180px] h-9 text-xs gap-1.5\">\n <ArrowDownUp className=\"h-3.5 w-3.5 text-muted-foreground\" />\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {(Object.keys(LABELS) as OrderSort[]).map((k) => (\n <SelectItem key={k} value={k} className=\"text-xs\">{LABELS[k]}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n"],"mappings":";AAgCM,SACE,KADF;AA9BN,SAAS,mBAAmB;AAE5B;AAAA,EACE;AAAA,EAAQ;AAAA,EAAe;AAAA,EAAY;AAAA,EAAe;AAAA,OAC7C;AAIP,MAAM,SAAoC;AAAA,EACxC,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,YAAY;AACd;AAIO,SAAS,WAAW,QAAoB,MAA6B;AAC1E,MAAI,SAAS,SAAU,QAAO;AAC9B,QAAM,YAAY,OAAO,OAAO,CAAC,MAAM,EAAE,MAAM,OAAO,IAAI;AAC1D,QAAM,eAAe,OAAO,OAAO,CAAC,MAAM,EAAE,MAAM,OAAO,IAAI;AAC7D,QAAM,SAAS,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM;AAC3C,UAAM,OAAO,OAAO,EAAE,MAAM,GAAI,IAAI,OAAO,EAAE,MAAM,GAAI;AACvD,WAAO,SAAS,cAAe,OAAO,KAAK,KAAK,OAAO,KAAK,IAAI,IAAM,OAAO,KAAK,KAAK,OAAO,KAAK,IAAI;AAAA,EACzG,CAAC;AACD,SAAO,CAAC,GAAG,QAAQ,GAAG,YAAY;AACpC;AAEO,SAAS,iBAAiB,EAAE,OAAO,SAAS,GAA2D;AAC5G,SACE,qBAAC,UAAO,OAAc,eAAe,CAAC,MAAM,SAAS,CAAc,GACjE;AAAA,yBAAC,iBAAc,WAAU,iCACvB;AAAA,0BAAC,eAAY,WAAU,qCAAoC;AAAA,MAC3D,oBAAC,eAAY;AAAA,OACf;AAAA,IACA,oBAAC,iBACG,iBAAO,KAAK,MAAM,EAAkB,IAAI,CAAC,MACzC,oBAAC,cAAmB,OAAO,GAAG,WAAU,WAAW,iBAAO,CAAC,KAA1C,CAA4C,CAC9D,GACH;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var portfolio_activity_exports = {};
|
|
21
|
+
__export(portfolio_activity_exports, {
|
|
22
|
+
PortfolioActivity: () => PortfolioActivity
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(portfolio_activity_exports);
|
|
25
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
26
|
+
var import_react = require("react");
|
|
27
|
+
var import_lucide_react = require("lucide-react");
|
|
28
|
+
var import_sdk = require("@medialane/sdk");
|
|
29
|
+
var import_use_activities = require("../utils/use-activities.js");
|
|
30
|
+
var import_empty_or_error = require("./empty-or-error.js");
|
|
31
|
+
var import_activity = require("../data/activity.js");
|
|
32
|
+
var import_activity_row = require("./activity-row.js");
|
|
33
|
+
var import_cn = require("../utils/cn.js");
|
|
34
|
+
const EXPLORER_URL = process.env.NEXT_PUBLIC_EXPLORER_URL || "https://voyager.online";
|
|
35
|
+
function PortfolioActivity({ getClient, address }) {
|
|
36
|
+
const { activities, isLoading, error, mutate } = (0, import_use_activities.useActivitiesByAddress)(getClient, address);
|
|
37
|
+
const [typeFilter, setTypeFilter] = (0, import_react.useState)("");
|
|
38
|
+
const displayed = typeFilter ? activities.filter((a) => a.type === typeFilter) : activities;
|
|
39
|
+
const typeCounts = (0, import_react.useMemo)(() => {
|
|
40
|
+
const counts = {};
|
|
41
|
+
for (const a of activities) {
|
|
42
|
+
counts[a.type] = (counts[a.type] ?? 0) + 1;
|
|
43
|
+
}
|
|
44
|
+
return counts;
|
|
45
|
+
}, [activities]);
|
|
46
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-4", children: [
|
|
47
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-wrap gap-2", children: import_activity.TYPE_FILTERS.map((f) => {
|
|
48
|
+
const typeConfig = f.value ? import_activity.ACTIVITY_TYPE_CONFIG[f.value] : null;
|
|
49
|
+
const Icon = typeConfig?.icon;
|
|
50
|
+
const count = f.value ? typeCounts[f.value] : activities.length;
|
|
51
|
+
const isActive = typeFilter === f.value;
|
|
52
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
53
|
+
"button",
|
|
54
|
+
{
|
|
55
|
+
onClick: () => setTypeFilter(f.value),
|
|
56
|
+
className: (0, import_cn.cn)(
|
|
57
|
+
"flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-full transition-all font-medium",
|
|
58
|
+
isActive ? "bg-primary text-primary-foreground shadow-sm" : "bg-muted text-muted-foreground hover:bg-muted/70 hover:text-foreground"
|
|
59
|
+
),
|
|
60
|
+
children: [
|
|
61
|
+
Icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
62
|
+
Icon,
|
|
63
|
+
{
|
|
64
|
+
className: (0, import_cn.cn)(
|
|
65
|
+
"h-3 w-3",
|
|
66
|
+
isActive ? "text-primary-foreground" : typeConfig?.colorClass
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
),
|
|
70
|
+
f.label,
|
|
71
|
+
!isLoading && count != null && count > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
72
|
+
"span",
|
|
73
|
+
{
|
|
74
|
+
className: (0, import_cn.cn)(
|
|
75
|
+
"text-[10px] font-bold tabular-nums",
|
|
76
|
+
isActive ? "text-primary-foreground/70" : "text-muted-foreground/60"
|
|
77
|
+
),
|
|
78
|
+
children: count
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
f.value
|
|
84
|
+
);
|
|
85
|
+
}) }),
|
|
86
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
87
|
+
import_empty_or_error.EmptyOrError,
|
|
88
|
+
{
|
|
89
|
+
isLoading,
|
|
90
|
+
error,
|
|
91
|
+
isEmpty: displayed.length === 0,
|
|
92
|
+
onRetry: mutate,
|
|
93
|
+
emptyTitle: typeFilter ? `No ${typeFilter} events yet` : "No activity yet",
|
|
94
|
+
emptyDescription: "Start by listing or buying an asset on the marketplace.",
|
|
95
|
+
emptyCta: { label: "Browse marketplace", href: "/marketplace" },
|
|
96
|
+
emptyIcon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Activity, { className: "h-7 w-7 text-muted-foreground" }),
|
|
97
|
+
skeletonCount: 8,
|
|
98
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rounded-xl border border-border overflow-hidden divide-y divide-border/50", children: displayed.map((activity, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
99
|
+
import_activity_row.ActivityRow,
|
|
100
|
+
{
|
|
101
|
+
activity,
|
|
102
|
+
showActor: false,
|
|
103
|
+
showExplorer: true,
|
|
104
|
+
explorerUrl: EXPLORER_URL,
|
|
105
|
+
getAssetHref: (c, t) => (0, import_sdk.assetHref)("STARKNET", c, t)
|
|
106
|
+
},
|
|
107
|
+
`${activity.txHash}-${activity.type}-${i}`
|
|
108
|
+
)) })
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
] });
|
|
112
|
+
}
|
|
113
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
114
|
+
0 && (module.exports = {
|
|
115
|
+
PortfolioActivity
|
|
116
|
+
});
|
|
117
|
+
//# sourceMappingURL=portfolio-activity.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/portfolio-activity.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useMemo } from \"react\";\nimport type { MedialaneClient } from \"@medialane/sdk/starknet\";\nimport { Activity } from \"lucide-react\";\nimport { assetHref } from \"@medialane/sdk\";\nimport { useActivitiesByAddress } from \"../utils/use-activities.js\";\nimport { EmptyOrError } from \"./empty-or-error.js\";\nimport { ACTIVITY_TYPE_CONFIG, TYPE_FILTERS } from \"../data/activity.js\";\nimport { ActivityRow } from \"./activity-row.js\";\nimport { cn } from \"../utils/cn.js\";\n\nconst EXPLORER_URL = process.env.NEXT_PUBLIC_EXPLORER_URL || \"https://voyager.online\";\n\nexport interface PortfolioActivityProps {\n getClient: () => MedialaneClient;\n address: string | null;\n}\n\nexport function PortfolioActivity({ getClient, address }: PortfolioActivityProps) {\n const { activities, isLoading, error, mutate } = useActivitiesByAddress(getClient, address);\n const [typeFilter, setTypeFilter] = useState(\"\");\n\n const displayed = typeFilter\n ? activities.filter((a) => a.type === typeFilter)\n : activities;\n\n // Per-type counts for the filter chip badges\n const typeCounts = useMemo(() => {\n const counts: Record<string, number> = {};\n for (const a of activities) {\n counts[a.type] = (counts[a.type] ?? 0) + 1;\n }\n return counts;\n }, [activities]);\n\n return (\n <div className=\"space-y-4\">\n {/* Filter chips with icons + per-type counts */}\n <div className=\"flex flex-wrap gap-2\">\n {TYPE_FILTERS.map((f) => {\n const typeConfig = f.value ? ACTIVITY_TYPE_CONFIG[f.value] : null;\n const Icon = typeConfig?.icon;\n const count = f.value ? typeCounts[f.value] : activities.length;\n const isActive = typeFilter === f.value;\n return (\n <button\n key={f.value}\n onClick={() => setTypeFilter(f.value)}\n className={cn(\n \"flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-full transition-all font-medium\",\n isActive\n ? \"bg-primary text-primary-foreground shadow-sm\"\n : \"bg-muted text-muted-foreground hover:bg-muted/70 hover:text-foreground\"\n )}\n >\n {Icon && (\n <Icon\n className={cn(\n \"h-3 w-3\",\n isActive ? \"text-primary-foreground\" : typeConfig?.colorClass\n )}\n />\n )}\n {f.label}\n {!isLoading && count != null && count > 0 && (\n <span\n className={cn(\n \"text-[10px] font-bold tabular-nums\",\n isActive\n ? \"text-primary-foreground/70\"\n : \"text-muted-foreground/60\"\n )}\n >\n {count}\n </span>\n )}\n </button>\n );\n })}\n </div>\n\n <EmptyOrError\n isLoading={isLoading}\n error={error}\n isEmpty={displayed.length === 0}\n onRetry={mutate}\n emptyTitle={typeFilter ? `No ${typeFilter} events yet` : \"No activity yet\"}\n emptyDescription=\"Start by listing or buying an asset on the marketplace.\"\n emptyCta={{ label: \"Browse marketplace\", href: \"/marketplace\" }}\n emptyIcon={<Activity className=\"h-7 w-7 text-muted-foreground\" />}\n skeletonCount={8}\n >\n <div className=\"rounded-xl border border-border overflow-hidden divide-y divide-border/50\">\n {displayed.map((activity, i) => (\n <ActivityRow\n key={`${activity.txHash}-${activity.type}-${i}`}\n activity={activity}\n showActor={false}\n showExplorer\n explorerUrl={EXPLORER_URL}\n getAssetHref={(c, t) => assetHref(\"STARKNET\", c, t)}\n />\n ))}\n </div>\n </EmptyOrError>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA8CY;AA5CZ,mBAAkC;AAElC,0BAAyB;AACzB,iBAA0B;AAC1B,4BAAuC;AACvC,4BAA6B;AAC7B,sBAAmD;AACnD,0BAA4B;AAC5B,gBAAmB;AAEnB,MAAM,eAAe,QAAQ,IAAI,4BAA4B;AAOtD,SAAS,kBAAkB,EAAE,WAAW,QAAQ,GAA2B;AAChF,QAAM,EAAE,YAAY,WAAW,OAAO,OAAO,QAAI,8CAAuB,WAAW,OAAO;AAC1F,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAS,EAAE;AAE/C,QAAM,YAAY,aACd,WAAW,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,IAC9C;AAGJ,QAAM,iBAAa,sBAAQ,MAAM;AAC/B,UAAM,SAAiC,CAAC;AACxC,eAAW,KAAK,YAAY;AAC1B,aAAO,EAAE,IAAI,KAAK,OAAO,EAAE,IAAI,KAAK,KAAK;AAAA,IAC3C;AACA,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,CAAC;AAEf,SACE,6CAAC,SAAI,WAAU,aAEb;AAAA,gDAAC,SAAI,WAAU,wBACZ,uCAAa,IAAI,CAAC,MAAM;AACvB,YAAM,aAAa,EAAE,QAAQ,qCAAqB,EAAE,KAAK,IAAI;AAC7D,YAAM,OAAO,YAAY;AACzB,YAAM,QAAQ,EAAE,QAAQ,WAAW,EAAE,KAAK,IAAI,WAAW;AACzD,YAAM,WAAW,eAAe,EAAE;AAClC,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,SAAS,MAAM,cAAc,EAAE,KAAK;AAAA,UACpC,eAAW;AAAA,YACT;AAAA,YACA,WACI,iDACA;AAAA,UACN;AAAA,UAEC;AAAA,oBACC;AAAA,cAAC;AAAA;AAAA,gBACC,eAAW;AAAA,kBACT;AAAA,kBACA,WAAW,4BAA4B,YAAY;AAAA,gBACrD;AAAA;AAAA,YACF;AAAA,YAED,EAAE;AAAA,YACF,CAAC,aAAa,SAAS,QAAQ,QAAQ,KACtC;AAAA,cAAC;AAAA;AAAA,gBACC,eAAW;AAAA,kBACT;AAAA,kBACA,WACI,+BACA;AAAA,gBACN;AAAA,gBAEC;AAAA;AAAA,YACH;AAAA;AAAA;AAAA,QA5BG,EAAE;AAAA,MA8BT;AAAA,IAEJ,CAAC,GACH;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,SAAS,UAAU,WAAW;AAAA,QAC9B,SAAS;AAAA,QACT,YAAY,aAAa,MAAM,UAAU,gBAAgB;AAAA,QACzD,kBAAiB;AAAA,QACjB,UAAU,EAAE,OAAO,sBAAsB,MAAM,eAAe;AAAA,QAC9D,WAAW,4CAAC,gCAAS,WAAU,iCAAgC;AAAA,QAC/D,eAAe;AAAA,QAEf,sDAAC,SAAI,WAAU,6EACZ,oBAAU,IAAI,CAAC,UAAU,MACxB;AAAA,UAAC;AAAA;AAAA,YAEC;AAAA,YACA,WAAW;AAAA,YACX,cAAY;AAAA,YACZ,aAAa;AAAA,YACb,cAAc,CAAC,GAAG,UAAM,sBAAU,YAAY,GAAG,CAAC;AAAA;AAAA,UAL7C,GAAG,SAAS,MAAM,IAAI,SAAS,IAAI,IAAI,CAAC;AAAA,QAM/C,CACD,GACH;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { MedialaneClient } from '@medialane/sdk/starknet';
|
|
3
|
+
|
|
4
|
+
interface PortfolioActivityProps {
|
|
5
|
+
getClient: () => MedialaneClient;
|
|
6
|
+
address: string | null;
|
|
7
|
+
}
|
|
8
|
+
declare function PortfolioActivity({ getClient, address }: PortfolioActivityProps): react_jsx_runtime.JSX.Element;
|
|
9
|
+
|
|
10
|
+
export { PortfolioActivity, type PortfolioActivityProps };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { MedialaneClient } from '@medialane/sdk/starknet';
|
|
3
|
+
|
|
4
|
+
interface PortfolioActivityProps {
|
|
5
|
+
getClient: () => MedialaneClient;
|
|
6
|
+
address: string | null;
|
|
7
|
+
}
|
|
8
|
+
declare function PortfolioActivity({ getClient, address }: PortfolioActivityProps): react_jsx_runtime.JSX.Element;
|
|
9
|
+
|
|
10
|
+
export { PortfolioActivity, type PortfolioActivityProps };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useMemo } from "react";
|
|
4
|
+
import { Activity } from "lucide-react";
|
|
5
|
+
import { assetHref } from "@medialane/sdk";
|
|
6
|
+
import { useActivitiesByAddress } from "../utils/use-activities.js";
|
|
7
|
+
import { EmptyOrError } from "./empty-or-error.js";
|
|
8
|
+
import { ACTIVITY_TYPE_CONFIG, TYPE_FILTERS } from "../data/activity.js";
|
|
9
|
+
import { ActivityRow } from "./activity-row.js";
|
|
10
|
+
import { cn } from "../utils/cn.js";
|
|
11
|
+
const EXPLORER_URL = process.env.NEXT_PUBLIC_EXPLORER_URL || "https://voyager.online";
|
|
12
|
+
function PortfolioActivity({ getClient, address }) {
|
|
13
|
+
const { activities, isLoading, error, mutate } = useActivitiesByAddress(getClient, address);
|
|
14
|
+
const [typeFilter, setTypeFilter] = useState("");
|
|
15
|
+
const displayed = typeFilter ? activities.filter((a) => a.type === typeFilter) : activities;
|
|
16
|
+
const typeCounts = useMemo(() => {
|
|
17
|
+
const counts = {};
|
|
18
|
+
for (const a of activities) {
|
|
19
|
+
counts[a.type] = (counts[a.type] ?? 0) + 1;
|
|
20
|
+
}
|
|
21
|
+
return counts;
|
|
22
|
+
}, [activities]);
|
|
23
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
24
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: TYPE_FILTERS.map((f) => {
|
|
25
|
+
const typeConfig = f.value ? ACTIVITY_TYPE_CONFIG[f.value] : null;
|
|
26
|
+
const Icon = typeConfig?.icon;
|
|
27
|
+
const count = f.value ? typeCounts[f.value] : activities.length;
|
|
28
|
+
const isActive = typeFilter === f.value;
|
|
29
|
+
return /* @__PURE__ */ jsxs(
|
|
30
|
+
"button",
|
|
31
|
+
{
|
|
32
|
+
onClick: () => setTypeFilter(f.value),
|
|
33
|
+
className: cn(
|
|
34
|
+
"flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-full transition-all font-medium",
|
|
35
|
+
isActive ? "bg-primary text-primary-foreground shadow-sm" : "bg-muted text-muted-foreground hover:bg-muted/70 hover:text-foreground"
|
|
36
|
+
),
|
|
37
|
+
children: [
|
|
38
|
+
Icon && /* @__PURE__ */ jsx(
|
|
39
|
+
Icon,
|
|
40
|
+
{
|
|
41
|
+
className: cn(
|
|
42
|
+
"h-3 w-3",
|
|
43
|
+
isActive ? "text-primary-foreground" : typeConfig?.colorClass
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
),
|
|
47
|
+
f.label,
|
|
48
|
+
!isLoading && count != null && count > 0 && /* @__PURE__ */ jsx(
|
|
49
|
+
"span",
|
|
50
|
+
{
|
|
51
|
+
className: cn(
|
|
52
|
+
"text-[10px] font-bold tabular-nums",
|
|
53
|
+
isActive ? "text-primary-foreground/70" : "text-muted-foreground/60"
|
|
54
|
+
),
|
|
55
|
+
children: count
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
f.value
|
|
61
|
+
);
|
|
62
|
+
}) }),
|
|
63
|
+
/* @__PURE__ */ jsx(
|
|
64
|
+
EmptyOrError,
|
|
65
|
+
{
|
|
66
|
+
isLoading,
|
|
67
|
+
error,
|
|
68
|
+
isEmpty: displayed.length === 0,
|
|
69
|
+
onRetry: mutate,
|
|
70
|
+
emptyTitle: typeFilter ? `No ${typeFilter} events yet` : "No activity yet",
|
|
71
|
+
emptyDescription: "Start by listing or buying an asset on the marketplace.",
|
|
72
|
+
emptyCta: { label: "Browse marketplace", href: "/marketplace" },
|
|
73
|
+
emptyIcon: /* @__PURE__ */ jsx(Activity, { className: "h-7 w-7 text-muted-foreground" }),
|
|
74
|
+
skeletonCount: 8,
|
|
75
|
+
children: /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-border overflow-hidden divide-y divide-border/50", children: displayed.map((activity, i) => /* @__PURE__ */ jsx(
|
|
76
|
+
ActivityRow,
|
|
77
|
+
{
|
|
78
|
+
activity,
|
|
79
|
+
showActor: false,
|
|
80
|
+
showExplorer: true,
|
|
81
|
+
explorerUrl: EXPLORER_URL,
|
|
82
|
+
getAssetHref: (c, t) => assetHref("STARKNET", c, t)
|
|
83
|
+
},
|
|
84
|
+
`${activity.txHash}-${activity.type}-${i}`
|
|
85
|
+
)) })
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
] });
|
|
89
|
+
}
|
|
90
|
+
export {
|
|
91
|
+
PortfolioActivity
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=portfolio-activity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/portfolio-activity.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useMemo } from \"react\";\nimport type { MedialaneClient } from \"@medialane/sdk/starknet\";\nimport { Activity } from \"lucide-react\";\nimport { assetHref } from \"@medialane/sdk\";\nimport { useActivitiesByAddress } from \"../utils/use-activities.js\";\nimport { EmptyOrError } from \"./empty-or-error.js\";\nimport { ACTIVITY_TYPE_CONFIG, TYPE_FILTERS } from \"../data/activity.js\";\nimport { ActivityRow } from \"./activity-row.js\";\nimport { cn } from \"../utils/cn.js\";\n\nconst EXPLORER_URL = process.env.NEXT_PUBLIC_EXPLORER_URL || \"https://voyager.online\";\n\nexport interface PortfolioActivityProps {\n getClient: () => MedialaneClient;\n address: string | null;\n}\n\nexport function PortfolioActivity({ getClient, address }: PortfolioActivityProps) {\n const { activities, isLoading, error, mutate } = useActivitiesByAddress(getClient, address);\n const [typeFilter, setTypeFilter] = useState(\"\");\n\n const displayed = typeFilter\n ? activities.filter((a) => a.type === typeFilter)\n : activities;\n\n // Per-type counts for the filter chip badges\n const typeCounts = useMemo(() => {\n const counts: Record<string, number> = {};\n for (const a of activities) {\n counts[a.type] = (counts[a.type] ?? 0) + 1;\n }\n return counts;\n }, [activities]);\n\n return (\n <div className=\"space-y-4\">\n {/* Filter chips with icons + per-type counts */}\n <div className=\"flex flex-wrap gap-2\">\n {TYPE_FILTERS.map((f) => {\n const typeConfig = f.value ? ACTIVITY_TYPE_CONFIG[f.value] : null;\n const Icon = typeConfig?.icon;\n const count = f.value ? typeCounts[f.value] : activities.length;\n const isActive = typeFilter === f.value;\n return (\n <button\n key={f.value}\n onClick={() => setTypeFilter(f.value)}\n className={cn(\n \"flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-full transition-all font-medium\",\n isActive\n ? \"bg-primary text-primary-foreground shadow-sm\"\n : \"bg-muted text-muted-foreground hover:bg-muted/70 hover:text-foreground\"\n )}\n >\n {Icon && (\n <Icon\n className={cn(\n \"h-3 w-3\",\n isActive ? \"text-primary-foreground\" : typeConfig?.colorClass\n )}\n />\n )}\n {f.label}\n {!isLoading && count != null && count > 0 && (\n <span\n className={cn(\n \"text-[10px] font-bold tabular-nums\",\n isActive\n ? \"text-primary-foreground/70\"\n : \"text-muted-foreground/60\"\n )}\n >\n {count}\n </span>\n )}\n </button>\n );\n })}\n </div>\n\n <EmptyOrError\n isLoading={isLoading}\n error={error}\n isEmpty={displayed.length === 0}\n onRetry={mutate}\n emptyTitle={typeFilter ? `No ${typeFilter} events yet` : \"No activity yet\"}\n emptyDescription=\"Start by listing or buying an asset on the marketplace.\"\n emptyCta={{ label: \"Browse marketplace\", href: \"/marketplace\" }}\n emptyIcon={<Activity className=\"h-7 w-7 text-muted-foreground\" />}\n skeletonCount={8}\n >\n <div className=\"rounded-xl border border-border overflow-hidden divide-y divide-border/50\">\n {displayed.map((activity, i) => (\n <ActivityRow\n key={`${activity.txHash}-${activity.type}-${i}`}\n activity={activity}\n showActor={false}\n showExplorer\n explorerUrl={EXPLORER_URL}\n getAssetHref={(c, t) => assetHref(\"STARKNET\", c, t)}\n />\n ))}\n </div>\n </EmptyOrError>\n </div>\n );\n}\n"],"mappings":";AA8CY,SAWI,KAXJ;AA5CZ,SAAS,UAAU,eAAe;AAElC,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,8BAA8B;AACvC,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB,oBAAoB;AACnD,SAAS,mBAAmB;AAC5B,SAAS,UAAU;AAEnB,MAAM,eAAe,QAAQ,IAAI,4BAA4B;AAOtD,SAAS,kBAAkB,EAAE,WAAW,QAAQ,GAA2B;AAChF,QAAM,EAAE,YAAY,WAAW,OAAO,OAAO,IAAI,uBAAuB,WAAW,OAAO;AAC1F,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,EAAE;AAE/C,QAAM,YAAY,aACd,WAAW,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,IAC9C;AAGJ,QAAM,aAAa,QAAQ,MAAM;AAC/B,UAAM,SAAiC,CAAC;AACxC,eAAW,KAAK,YAAY;AAC1B,aAAO,EAAE,IAAI,KAAK,OAAO,EAAE,IAAI,KAAK,KAAK;AAAA,IAC3C;AACA,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,CAAC;AAEf,SACE,qBAAC,SAAI,WAAU,aAEb;AAAA,wBAAC,SAAI,WAAU,wBACZ,uBAAa,IAAI,CAAC,MAAM;AACvB,YAAM,aAAa,EAAE,QAAQ,qBAAqB,EAAE,KAAK,IAAI;AAC7D,YAAM,OAAO,YAAY;AACzB,YAAM,QAAQ,EAAE,QAAQ,WAAW,EAAE,KAAK,IAAI,WAAW;AACzD,YAAM,WAAW,eAAe,EAAE;AAClC,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,SAAS,MAAM,cAAc,EAAE,KAAK;AAAA,UACpC,WAAW;AAAA,YACT;AAAA,YACA,WACI,iDACA;AAAA,UACN;AAAA,UAEC;AAAA,oBACC;AAAA,cAAC;AAAA;AAAA,gBACC,WAAW;AAAA,kBACT;AAAA,kBACA,WAAW,4BAA4B,YAAY;AAAA,gBACrD;AAAA;AAAA,YACF;AAAA,YAED,EAAE;AAAA,YACF,CAAC,aAAa,SAAS,QAAQ,QAAQ,KACtC;AAAA,cAAC;AAAA;AAAA,gBACC,WAAW;AAAA,kBACT;AAAA,kBACA,WACI,+BACA;AAAA,gBACN;AAAA,gBAEC;AAAA;AAAA,YACH;AAAA;AAAA;AAAA,QA5BG,EAAE;AAAA,MA8BT;AAAA,IAEJ,CAAC,GACH;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,SAAS,UAAU,WAAW;AAAA,QAC9B,SAAS;AAAA,QACT,YAAY,aAAa,MAAM,UAAU,gBAAgB;AAAA,QACzD,kBAAiB;AAAA,QACjB,UAAU,EAAE,OAAO,sBAAsB,MAAM,eAAe;AAAA,QAC9D,WAAW,oBAAC,YAAS,WAAU,iCAAgC;AAAA,QAC/D,eAAe;AAAA,QAEf,8BAAC,SAAI,WAAU,6EACZ,oBAAU,IAAI,CAAC,UAAU,MACxB;AAAA,UAAC;AAAA;AAAA,YAEC;AAAA,YACA,WAAW;AAAA,YACX,cAAY;AAAA,YACZ,aAAa;AAAA,YACb,cAAc,CAAC,GAAG,MAAM,UAAU,YAAY,GAAG,CAAC;AAAA;AAAA,UAL7C,GAAG,SAAS,MAAM,IAAI,SAAS,IAAI,IAAI,CAAC;AAAA,QAM/C,CACD,GACH;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var price_history_chart_exports = {};
|
|
21
|
+
__export(price_history_chart_exports, {
|
|
22
|
+
PriceHistoryChart: () => PriceHistoryChart
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(price_history_chart_exports);
|
|
25
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
26
|
+
var import_react = require("react");
|
|
27
|
+
var import_recharts = require("recharts");
|
|
28
|
+
function parsePrice(formatted) {
|
|
29
|
+
return parseFloat(formatted.replace(/[^0-9.]/g, "")) || 0;
|
|
30
|
+
}
|
|
31
|
+
function PriceHistoryChart({ history }) {
|
|
32
|
+
const chartData = (0, import_react.useMemo)(() => {
|
|
33
|
+
const sales = history.filter(
|
|
34
|
+
(e) => e.type === "sale" && e.price?.formatted != null
|
|
35
|
+
);
|
|
36
|
+
if (sales.length < 2) return null;
|
|
37
|
+
const currencyCounts = /* @__PURE__ */ new Map();
|
|
38
|
+
for (const s of sales) {
|
|
39
|
+
const c = s.price.currency ?? "STRK";
|
|
40
|
+
currencyCounts.set(c, (currencyCounts.get(c) ?? 0) + 1);
|
|
41
|
+
}
|
|
42
|
+
const topCurrency = [...currencyCounts.entries()].sort(
|
|
43
|
+
(a, b) => b[1] - a[1]
|
|
44
|
+
)[0][0];
|
|
45
|
+
const points = sales.filter((s) => (s.price.currency ?? "STRK") === topCurrency).map((s) => ({
|
|
46
|
+
date: new Date(
|
|
47
|
+
typeof s.timestamp === "number" ? s.timestamp * 1e3 : s.timestamp
|
|
48
|
+
).toLocaleDateString("en-US", { month: "short", day: "numeric" }),
|
|
49
|
+
price: parsePrice(s.price.formatted ?? "")
|
|
50
|
+
})).reverse();
|
|
51
|
+
if (points.length < 2) return null;
|
|
52
|
+
return { currency: topCurrency, points };
|
|
53
|
+
}, [history]);
|
|
54
|
+
if (!chartData) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rounded-xl bg-card/50 p-4 space-y-2", children: [
|
|
58
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide", children: [
|
|
59
|
+
"Price history \xB7 ",
|
|
60
|
+
chartData.currency
|
|
61
|
+
] }),
|
|
62
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.ResponsiveContainer, { width: "100%", height: 160, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
63
|
+
import_recharts.LineChart,
|
|
64
|
+
{
|
|
65
|
+
data: chartData.points,
|
|
66
|
+
margin: { top: 4, right: 8, bottom: 0, left: 0 },
|
|
67
|
+
children: [
|
|
68
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
69
|
+
import_recharts.CartesianGrid,
|
|
70
|
+
{
|
|
71
|
+
strokeDasharray: "3 3",
|
|
72
|
+
stroke: "hsl(var(--border))",
|
|
73
|
+
opacity: 0.5
|
|
74
|
+
}
|
|
75
|
+
),
|
|
76
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
77
|
+
import_recharts.XAxis,
|
|
78
|
+
{
|
|
79
|
+
dataKey: "date",
|
|
80
|
+
tick: { fontSize: 11, fill: "hsl(var(--muted-foreground))" },
|
|
81
|
+
axisLine: false,
|
|
82
|
+
tickLine: false
|
|
83
|
+
}
|
|
84
|
+
),
|
|
85
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
86
|
+
import_recharts.YAxis,
|
|
87
|
+
{
|
|
88
|
+
tick: { fontSize: 11, fill: "hsl(var(--muted-foreground))" },
|
|
89
|
+
axisLine: false,
|
|
90
|
+
tickLine: false,
|
|
91
|
+
width: 48
|
|
92
|
+
}
|
|
93
|
+
),
|
|
94
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
95
|
+
import_recharts.Tooltip,
|
|
96
|
+
{
|
|
97
|
+
contentStyle: {
|
|
98
|
+
background: "hsl(var(--background))",
|
|
99
|
+
border: "1px solid hsl(var(--border))",
|
|
100
|
+
borderRadius: "8px",
|
|
101
|
+
fontSize: "12px",
|
|
102
|
+
color: "hsl(var(--foreground))"
|
|
103
|
+
},
|
|
104
|
+
formatter: (value) => [
|
|
105
|
+
`${value} ${chartData.currency}`,
|
|
106
|
+
"Sale price"
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
),
|
|
110
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
111
|
+
import_recharts.Line,
|
|
112
|
+
{
|
|
113
|
+
type: "monotone",
|
|
114
|
+
dataKey: "price",
|
|
115
|
+
stroke: "hsl(var(--primary))",
|
|
116
|
+
strokeWidth: 2,
|
|
117
|
+
dot: { fill: "hsl(var(--primary))", r: 3, strokeWidth: 0 },
|
|
118
|
+
activeDot: { r: 5, fill: "hsl(var(--primary))" }
|
|
119
|
+
}
|
|
120
|
+
)
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
) })
|
|
124
|
+
] });
|
|
125
|
+
}
|
|
126
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
127
|
+
0 && (module.exports = {
|
|
128
|
+
PriceHistoryChart
|
|
129
|
+
});
|
|
130
|
+
//# sourceMappingURL=price-history-chart.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/price-history-chart.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo } from \"react\";\nimport {\n LineChart,\n Line,\n XAxis,\n YAxis,\n Tooltip,\n ResponsiveContainer,\n CartesianGrid,\n} from \"recharts\";\nimport type { ApiActivity } from \"@medialane/sdk\";\n\nexport interface PriceHistoryChartProps {\n history: ApiActivity[];\n}\n\nfunction parsePrice(formatted: string): number {\n return parseFloat(formatted.replace(/[^0-9.]/g, \"\")) || 0;\n}\n\nexport function PriceHistoryChart({ history }: PriceHistoryChartProps) {\n const chartData = useMemo(() => {\n const sales = history.filter(\n (e) => e.type === \"sale\" && e.price?.formatted != null\n );\n if (sales.length < 2) return null;\n\n // Pick most common currency to keep Y-axis consistent\n const currencyCounts = new Map<string, number>();\n for (const s of sales) {\n const c = s.price!.currency ?? \"STRK\";\n currencyCounts.set(c, (currencyCounts.get(c) ?? 0) + 1);\n }\n const topCurrency = [...currencyCounts.entries()].sort(\n (a, b) => b[1] - a[1]\n )[0][0];\n\n const points = sales\n .filter((s) => (s.price!.currency ?? \"STRK\") === topCurrency)\n .map((s) => ({\n date: new Date(\n typeof s.timestamp === \"number\" ? s.timestamp * 1000 : s.timestamp\n ).toLocaleDateString(\"en-US\", { month: \"short\", day: \"numeric\" }),\n price: parsePrice(s.price!.formatted ?? \"\"),\n }))\n .reverse(); // oldest → newest left to right\n\n if (points.length < 2) return null;\n return { currency: topCurrency, points };\n }, [history]);\n\n if (!chartData) {\n return null;\n }\n\n return (\n <div className=\"rounded-xl bg-card/50 p-4 space-y-2\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Price history · {chartData.currency}\n </p>\n <ResponsiveContainer width=\"100%\" height={160}>\n <LineChart\n data={chartData.points}\n margin={{ top: 4, right: 8, bottom: 0, left: 0 }}\n >\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"hsl(var(--border))\"\n opacity={0.5}\n />\n <XAxis\n dataKey=\"date\"\n tick={{ fontSize: 11, fill: \"hsl(var(--muted-foreground))\" }}\n axisLine={false}\n tickLine={false}\n />\n <YAxis\n tick={{ fontSize: 11, fill: \"hsl(var(--muted-foreground))\" }}\n axisLine={false}\n tickLine={false}\n width={48}\n />\n <Tooltip\n contentStyle={{\n background: \"hsl(var(--background))\",\n border: \"1px solid hsl(var(--border))\",\n borderRadius: \"8px\",\n fontSize: \"12px\",\n color: \"hsl(var(--foreground))\",\n }}\n formatter={(value) => [\n `${value} ${chartData.currency}`,\n \"Sale price\",\n ]}\n />\n <Line\n type=\"monotone\"\n dataKey=\"price\"\n stroke=\"hsl(var(--primary))\"\n strokeWidth={2}\n dot={{ fill: \"hsl(var(--primary))\", r: 3, strokeWidth: 0 }}\n activeDot={{ r: 5, fill: \"hsl(var(--primary))\" }}\n />\n </LineChart>\n </ResponsiveContainer>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ApiActivity } from '@medialane/sdk';
|
|
3
|
+
|
|
4
|
+
interface PriceHistoryChartProps {
|
|
5
|
+
history: ApiActivity[];
|
|
6
|
+
}
|
|
7
|
+
declare function PriceHistoryChart({ history }: PriceHistoryChartProps): react_jsx_runtime.JSX.Element | null;
|
|
8
|
+
|
|
9
|
+
export { PriceHistoryChart, type PriceHistoryChartProps };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ApiActivity } from '@medialane/sdk';
|
|
3
|
+
|
|
4
|
+
interface PriceHistoryChartProps {
|
|
5
|
+
history: ApiActivity[];
|
|
6
|
+
}
|
|
7
|
+
declare function PriceHistoryChart({ history }: PriceHistoryChartProps): react_jsx_runtime.JSX.Element | null;
|
|
8
|
+
|
|
9
|
+
export { PriceHistoryChart, type PriceHistoryChartProps };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import {
|
|
5
|
+
LineChart,
|
|
6
|
+
Line,
|
|
7
|
+
XAxis,
|
|
8
|
+
YAxis,
|
|
9
|
+
Tooltip,
|
|
10
|
+
ResponsiveContainer,
|
|
11
|
+
CartesianGrid
|
|
12
|
+
} from "recharts";
|
|
13
|
+
function parsePrice(formatted) {
|
|
14
|
+
return parseFloat(formatted.replace(/[^0-9.]/g, "")) || 0;
|
|
15
|
+
}
|
|
16
|
+
function PriceHistoryChart({ history }) {
|
|
17
|
+
const chartData = useMemo(() => {
|
|
18
|
+
const sales = history.filter(
|
|
19
|
+
(e) => e.type === "sale" && e.price?.formatted != null
|
|
20
|
+
);
|
|
21
|
+
if (sales.length < 2) return null;
|
|
22
|
+
const currencyCounts = /* @__PURE__ */ new Map();
|
|
23
|
+
for (const s of sales) {
|
|
24
|
+
const c = s.price.currency ?? "STRK";
|
|
25
|
+
currencyCounts.set(c, (currencyCounts.get(c) ?? 0) + 1);
|
|
26
|
+
}
|
|
27
|
+
const topCurrency = [...currencyCounts.entries()].sort(
|
|
28
|
+
(a, b) => b[1] - a[1]
|
|
29
|
+
)[0][0];
|
|
30
|
+
const points = sales.filter((s) => (s.price.currency ?? "STRK") === topCurrency).map((s) => ({
|
|
31
|
+
date: new Date(
|
|
32
|
+
typeof s.timestamp === "number" ? s.timestamp * 1e3 : s.timestamp
|
|
33
|
+
).toLocaleDateString("en-US", { month: "short", day: "numeric" }),
|
|
34
|
+
price: parsePrice(s.price.formatted ?? "")
|
|
35
|
+
})).reverse();
|
|
36
|
+
if (points.length < 2) return null;
|
|
37
|
+
return { currency: topCurrency, points };
|
|
38
|
+
}, [history]);
|
|
39
|
+
if (!chartData) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-xl bg-card/50 p-4 space-y-2", children: [
|
|
43
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide", children: [
|
|
44
|
+
"Price history \xB7 ",
|
|
45
|
+
chartData.currency
|
|
46
|
+
] }),
|
|
47
|
+
/* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: 160, children: /* @__PURE__ */ jsxs(
|
|
48
|
+
LineChart,
|
|
49
|
+
{
|
|
50
|
+
data: chartData.points,
|
|
51
|
+
margin: { top: 4, right: 8, bottom: 0, left: 0 },
|
|
52
|
+
children: [
|
|
53
|
+
/* @__PURE__ */ jsx(
|
|
54
|
+
CartesianGrid,
|
|
55
|
+
{
|
|
56
|
+
strokeDasharray: "3 3",
|
|
57
|
+
stroke: "hsl(var(--border))",
|
|
58
|
+
opacity: 0.5
|
|
59
|
+
}
|
|
60
|
+
),
|
|
61
|
+
/* @__PURE__ */ jsx(
|
|
62
|
+
XAxis,
|
|
63
|
+
{
|
|
64
|
+
dataKey: "date",
|
|
65
|
+
tick: { fontSize: 11, fill: "hsl(var(--muted-foreground))" },
|
|
66
|
+
axisLine: false,
|
|
67
|
+
tickLine: false
|
|
68
|
+
}
|
|
69
|
+
),
|
|
70
|
+
/* @__PURE__ */ jsx(
|
|
71
|
+
YAxis,
|
|
72
|
+
{
|
|
73
|
+
tick: { fontSize: 11, fill: "hsl(var(--muted-foreground))" },
|
|
74
|
+
axisLine: false,
|
|
75
|
+
tickLine: false,
|
|
76
|
+
width: 48
|
|
77
|
+
}
|
|
78
|
+
),
|
|
79
|
+
/* @__PURE__ */ jsx(
|
|
80
|
+
Tooltip,
|
|
81
|
+
{
|
|
82
|
+
contentStyle: {
|
|
83
|
+
background: "hsl(var(--background))",
|
|
84
|
+
border: "1px solid hsl(var(--border))",
|
|
85
|
+
borderRadius: "8px",
|
|
86
|
+
fontSize: "12px",
|
|
87
|
+
color: "hsl(var(--foreground))"
|
|
88
|
+
},
|
|
89
|
+
formatter: (value) => [
|
|
90
|
+
`${value} ${chartData.currency}`,
|
|
91
|
+
"Sale price"
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
),
|
|
95
|
+
/* @__PURE__ */ jsx(
|
|
96
|
+
Line,
|
|
97
|
+
{
|
|
98
|
+
type: "monotone",
|
|
99
|
+
dataKey: "price",
|
|
100
|
+
stroke: "hsl(var(--primary))",
|
|
101
|
+
strokeWidth: 2,
|
|
102
|
+
dot: { fill: "hsl(var(--primary))", r: 3, strokeWidth: 0 },
|
|
103
|
+
activeDot: { r: 5, fill: "hsl(var(--primary))" }
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
) })
|
|
109
|
+
] });
|
|
110
|
+
}
|
|
111
|
+
export {
|
|
112
|
+
PriceHistoryChart
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=price-history-chart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/price-history-chart.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo } from \"react\";\nimport {\n LineChart,\n Line,\n XAxis,\n YAxis,\n Tooltip,\n ResponsiveContainer,\n CartesianGrid,\n} from \"recharts\";\nimport type { ApiActivity } from \"@medialane/sdk\";\n\nexport interface PriceHistoryChartProps {\n history: ApiActivity[];\n}\n\nfunction parsePrice(formatted: string): number {\n return parseFloat(formatted.replace(/[^0-9.]/g, \"\")) || 0;\n}\n\nexport function PriceHistoryChart({ history }: PriceHistoryChartProps) {\n const chartData = useMemo(() => {\n const sales = history.filter(\n (e) => e.type === \"sale\" && e.price?.formatted != null\n );\n if (sales.length < 2) return null;\n\n // Pick most common currency to keep Y-axis consistent\n const currencyCounts = new Map<string, number>();\n for (const s of sales) {\n const c = s.price!.currency ?? \"STRK\";\n currencyCounts.set(c, (currencyCounts.get(c) ?? 0) + 1);\n }\n const topCurrency = [...currencyCounts.entries()].sort(\n (a, b) => b[1] - a[1]\n )[0][0];\n\n const points = sales\n .filter((s) => (s.price!.currency ?? \"STRK\") === topCurrency)\n .map((s) => ({\n date: new Date(\n typeof s.timestamp === \"number\" ? s.timestamp * 1000 : s.timestamp\n ).toLocaleDateString(\"en-US\", { month: \"short\", day: \"numeric\" }),\n price: parsePrice(s.price!.formatted ?? \"\"),\n }))\n .reverse(); // oldest → newest left to right\n\n if (points.length < 2) return null;\n return { currency: topCurrency, points };\n }, [history]);\n\n if (!chartData) {\n return null;\n }\n\n return (\n <div className=\"rounded-xl bg-card/50 p-4 space-y-2\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Price history · {chartData.currency}\n </p>\n <ResponsiveContainer width=\"100%\" height={160}>\n <LineChart\n data={chartData.points}\n margin={{ top: 4, right: 8, bottom: 0, left: 0 }}\n >\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"hsl(var(--border))\"\n opacity={0.5}\n />\n <XAxis\n dataKey=\"date\"\n tick={{ fontSize: 11, fill: \"hsl(var(--muted-foreground))\" }}\n axisLine={false}\n tickLine={false}\n />\n <YAxis\n tick={{ fontSize: 11, fill: \"hsl(var(--muted-foreground))\" }}\n axisLine={false}\n tickLine={false}\n width={48}\n />\n <Tooltip\n contentStyle={{\n background: \"hsl(var(--background))\",\n border: \"1px solid hsl(var(--border))\",\n borderRadius: \"8px\",\n fontSize: \"12px\",\n color: \"hsl(var(--foreground))\",\n }}\n formatter={(value) => [\n `${value} ${chartData.currency}`,\n \"Sale price\",\n ]}\n />\n <Line\n type=\"monotone\"\n dataKey=\"price\"\n stroke=\"hsl(var(--primary))\"\n strokeWidth={2}\n dot={{ fill: \"hsl(var(--primary))\", r: 3, strokeWidth: 0 }}\n activeDot={{ r: 5, fill: \"hsl(var(--primary))\" }}\n />\n </LineChart>\n </ResponsiveContainer>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|