@loafmarkets/ui 0.0.5 → 0.0.7

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/index.js CHANGED
@@ -168,8 +168,8 @@ var PortfolioSummary = React5__namespace.forwardRef(
168
168
  onResetAccount,
169
169
  positionsHeading = "Current Positions",
170
170
  emptyPositionsText = "No positions yet. Start trading to build your portfolio!",
171
- formatCurrency = defaultFormatCurrency,
172
- formatPercent = defaultFormatPercent,
171
+ formatCurrency: formatCurrency2 = defaultFormatCurrency,
172
+ formatPercent: formatPercent2 = defaultFormatPercent,
173
173
  formatSignedCurrency = defaultFormatSignedCurrency,
174
174
  className,
175
175
  children,
@@ -206,15 +206,15 @@ var PortfolioSummary = React5__namespace.forwardRef(
206
206
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
207
207
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
208
208
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs uppercase tracking-[0.5px] text-white/50", children: "Available Cash" }),
209
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-[1.25rem] font-semibold text-white", children: formatCurrency(availableCash) })
209
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-[1.25rem] font-semibold text-white", children: formatCurrency2(availableCash) })
210
210
  ] }),
211
211
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
212
212
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs uppercase tracking-[0.5px] text-white/50", children: "Portfolio Value" }),
213
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-[1.25rem] font-semibold text-white", children: formatCurrency(portfolioValue) })
213
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-[1.25rem] font-semibold text-white", children: formatCurrency2(portfolioValue) })
214
214
  ] }),
215
215
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
216
216
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs uppercase tracking-[0.5px] text-white/50", children: "Total Return" }),
217
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("mt-1 text-[1.25rem] font-semibold", totalReturnClassName), children: formatPercent(totalReturnPercent) })
217
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("mt-1 text-[1.25rem] font-semibold", totalReturnClassName), children: formatPercent2(totalReturnPercent) })
218
218
  ] }),
219
219
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
220
220
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs uppercase tracking-[0.5px] text-white/50", children: "Unrealized P&L" }),
@@ -304,6 +304,7 @@ function HousePositionSlider({
304
304
  pendingOrders = [],
305
305
  defaultOrderType = "market",
306
306
  orderbook,
307
+ ownershipPercentOverride,
307
308
  onConfirmOrder,
308
309
  className,
309
310
  ...props
@@ -314,6 +315,7 @@ function HousePositionSlider({
314
315
  const [deltaTokensBuy, setDeltaTokensBuy] = React5__namespace.useState(0);
315
316
  const [deltaTokensSell, setDeltaTokensSell] = React5__namespace.useState(0);
316
317
  const [isDragging, setIsDragging] = React5__namespace.useState(false);
318
+ const [visualTargetPct, setVisualTargetPct] = React5__namespace.useState(null);
317
319
  const [orderType, setOrderType] = React5__namespace.useState(defaultOrderType);
318
320
  const [limitPrice, setLimitPrice] = React5__namespace.useState(currentPrice);
319
321
  const [limitPriceInput, setLimitPriceInput] = React5__namespace.useState(currentPrice.toFixed(2));
@@ -335,9 +337,8 @@ function HousePositionSlider({
335
337
  const effectiveAvailableCash = Math.max(0, availableCash - pendingBuyValue);
336
338
  const effectiveTokensHeld = Math.max(0, tokensHeld - pendingSellTokens);
337
339
  const holdingsValue = tokensHeld * effectivePrice;
338
- const sliderHoldingsValue = effectiveTokensHeld * effectivePrice;
339
- const sliderTotalCapacity = sliderHoldingsValue + effectiveAvailableCash;
340
- const baselinePct = sliderTotalCapacity <= 0 ? 0 : sliderHoldingsValue / sliderTotalCapacity * 100;
340
+ const safeTotalTokens = totalTokens > 0 ? totalTokens : 1;
341
+ const baselineOwnershipActual = clamp(effectiveTokensHeld / safeTotalTokens * 100, 0, 100);
341
342
  let deltaTokens = 0;
342
343
  let deltaValue = 0;
343
344
  let marketAvgPrice = null;
@@ -383,29 +384,21 @@ function HousePositionSlider({
383
384
  }
384
385
  targetTokens = tokensHeld + deltaTokens;
385
386
  targetValue = targetTokens * effectivePrice;
386
- const plannedDeltaValue = (() => {
387
- if (orderMode === "buy") {
388
- if (buyTrackingMode === "dollars") {
389
- const notional = Math.min(Math.max(0, deltaDollars), effectiveAvailableCash);
390
- return notional;
391
- }
392
- const tokensPlanned = Math.max(0, deltaTokensBuy);
393
- const referencePrice = orderType === "market" ? currentPrice || limitPriceSafe : limitPriceSafe;
394
- return Math.min(tokensPlanned * referencePrice, effectiveAvailableCash);
395
- }
396
- if (orderMode === "sell") {
397
- const tokensToSell = Math.abs(Math.min(0, deltaTokensSell));
398
- const sellValue = tokensToSell * effectivePrice;
399
- return -Math.min(sellValue, sliderHoldingsValue);
400
- }
401
- return 0;
402
- })();
403
- const sliderTargetValue = clamp(sliderHoldingsValue + plannedDeltaValue, 0, sliderTotalCapacity);
404
- const targetPct = sliderTotalCapacity <= 0 ? 0 : sliderTargetValue / sliderTotalCapacity * 100;
387
+ const sliderTargetTokens = clamp(effectiveTokensHeld + deltaTokens, 0, safeTotalTokens);
388
+ const normalizedTargetPct = sliderTargetTokens / safeTotalTokens * 100;
405
389
  const isIncrease = orderMode === "buy";
406
390
  const hasChange = orderMode !== "none" && (Math.abs(deltaTokens) > 1e-3 || Math.abs(deltaValue) > 0.01);
407
- const currentOwnership = totalTokens <= 0 ? 0 : tokensHeld / totalTokens * 100;
408
- const targetOwnership = totalTokens <= 0 ? 0 : targetTokens / totalTokens * 100;
391
+ const currentOwnership = totalTokens <= 0 ? 0 : clamp(tokensHeld / totalTokens * 100, 0, 100);
392
+ const targetOwnership = totalTokens <= 0 ? 0 : clamp(targetTokens / totalTokens * 100, 0, 100);
393
+ const ownershipOverrideValue = typeof ownershipPercentOverride === "number" && Number.isFinite(ownershipPercentOverride) ? clamp(ownershipPercentOverride, 0, 100) : null;
394
+ const ownershipShift = ownershipOverrideValue != null ? ownershipOverrideValue - baselineOwnershipActual : 0;
395
+ const baselinePct = clamp(ownershipOverrideValue ?? baselineOwnershipActual, 0, 100);
396
+ const impliedTargetPct = clamp(normalizedTargetPct + ownershipShift, 0, 100);
397
+ const displayTargetPct = visualTargetPct ?? impliedTargetPct;
398
+ const targetPct = displayTargetPct;
399
+ const displayCurrentOwnership = clamp(ownershipOverrideValue ?? currentOwnership, 0, 100);
400
+ const impliedDisplayTargetOwnership = clamp(targetOwnership + ownershipShift, 0, 100);
401
+ const displayTargetOwnership = visualTargetPct ?? impliedDisplayTargetOwnership;
409
402
  const estFeeTokens = Math.abs(deltaValue) * 5e-3 / (effectivePrice || 1);
410
403
  const resetOrder = React5__namespace.useCallback(() => {
411
404
  setOrderMode("none");
@@ -413,6 +406,7 @@ function HousePositionSlider({
413
406
  setDeltaDollars(0);
414
407
  setDeltaTokensBuy(0);
415
408
  setDeltaTokensSell(0);
409
+ setVisualTargetPct(null);
416
410
  }, []);
417
411
  const updateOrderFromTargetValue = React5__namespace.useCallback(
418
412
  (newTargetValue) => {
@@ -443,6 +437,7 @@ function HousePositionSlider({
443
437
  const nextOwnership = clamp(newOwnershipPercent, 0, 100);
444
438
  const newTargetTokens = nextOwnership / 100 * totalTokens;
445
439
  updateOrderFromTargetValue(newTargetTokens * effectivePrice);
440
+ setVisualTargetPct(nextOwnership);
446
441
  },
447
442
  [effectivePrice, totalTokens, updateOrderFromTargetValue]
448
443
  );
@@ -475,12 +470,14 @@ function HousePositionSlider({
475
470
  const magnitude = Math.min(Math.abs(normalized), 1);
476
471
  if (magnitude < 0.02) {
477
472
  resetOrder();
473
+ setVisualTargetPct(null);
478
474
  return;
479
475
  }
480
476
  if (normalized > 0) {
481
477
  const notional = clamp(magnitude * effectiveAvailableCash, 0, effectiveAvailableCash);
482
478
  if (notional <= 0) {
483
479
  resetOrder();
480
+ setVisualTargetPct(null);
484
481
  return;
485
482
  }
486
483
  setOrderMode("buy");
@@ -488,12 +485,14 @@ function HousePositionSlider({
488
485
  setDeltaDollars(notional);
489
486
  setDeltaTokensBuy(0);
490
487
  setDeltaTokensSell(0);
488
+ setVisualTargetPct(clamp(pct, 0, 100));
491
489
  return;
492
490
  }
493
491
  if (normalized < 0) {
494
492
  const tokensToSell = clamp(magnitude * effectiveTokensHeld, 0, effectiveTokensHeld);
495
493
  if (tokensToSell <= 0) {
496
494
  resetOrder();
495
+ setVisualTargetPct(null);
497
496
  return;
498
497
  }
499
498
  setOrderMode("sell");
@@ -501,9 +500,11 @@ function HousePositionSlider({
501
500
  setDeltaTokensSell(-tokensToSell);
502
501
  setDeltaDollars(0);
503
502
  setDeltaTokensBuy(0);
503
+ setVisualTargetPct(clamp(pct, 0, 100));
504
504
  return;
505
505
  }
506
506
  resetOrder();
507
+ setVisualTargetPct(null);
507
508
  },
508
509
  [effectiveAvailableCash, effectiveTokensHeld, resetOrder]
509
510
  );
@@ -666,19 +667,19 @@ function HousePositionSlider({
666
667
  " Ownership"
667
668
  ] }),
668
669
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-white", children: [
669
- currentOwnership.toFixed(2),
670
+ displayCurrentOwnership.toFixed(2),
670
671
  "%",
671
672
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mx-1.5 text-white/50", children: "\u2192" }),
672
673
  /* @__PURE__ */ jsxRuntime.jsx(
673
674
  "input",
674
675
  {
675
676
  type: "text",
676
- value: ownershipInput || targetOwnership.toFixed(2),
677
+ value: ownershipInput || displayTargetOwnership.toFixed(2),
677
678
  onChange: (e) => {
678
679
  const val = e.target.value;
679
680
  if (val === "" || /^[0-9]*\.?[0-9]*$/.test(val)) setOwnershipInput(val);
680
681
  },
681
- onFocus: () => setOwnershipInput(targetOwnership.toFixed(2)),
682
+ onFocus: () => setOwnershipInput(displayTargetOwnership.toFixed(2)),
682
683
  onBlur: () => {
683
684
  const num = Number.parseFloat(ownershipInput);
684
685
  if (Number.isFinite(num)) updateOrderFromOwnership(num);
@@ -689,7 +690,7 @@ function HousePositionSlider({
689
690
  },
690
691
  className: cn(
691
692
  "w-[70px] rounded-[4px] border bg-white/10 px-2 py-1 text-right font-semibold outline-none",
692
- targetOwnership >= currentOwnership ? "border-[rgba(14,203,129,0.3)] text-[#0ecb81] focus:border-[#0ecb81]" : "border-[rgba(246,70,93,0.3)] text-[#f6465d] focus:border-[#f6465d]"
693
+ displayTargetOwnership >= displayCurrentOwnership ? "border-[rgba(14,203,129,0.3)] text-[#0ecb81] focus:border-[#0ecb81]" : "border-[rgba(246,70,93,0.3)] text-[#f6465d] focus:border-[#f6465d]"
693
694
  )
694
695
  }
695
696
  )
@@ -840,6 +841,9 @@ function DepthRow({
840
841
  ] });
841
842
  }
842
843
  var clamp2 = (value, min, max) => Math.min(max, Math.max(min, value));
844
+ var LEVEL_ROWS_VISIBLE = 6;
845
+ var DEPTH_ROW_HEIGHT_PX = 34;
846
+ var SECTION_HEIGHT = LEVEL_ROWS_VISIBLE * DEPTH_ROW_HEIGHT_PX;
843
847
  var Orderbook = React5__namespace.forwardRef(
844
848
  ({
845
849
  asks,
@@ -980,18 +984,25 @@ var Orderbook = React5__namespace.forwardRef(
980
984
  ] }, `${trade.type}-${trade.price}-${trade.amount}-${trade.time ?? i}`)) })
981
985
  }
982
986
  ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
983
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-white/5", children: asks.map((l, idx) => /* @__PURE__ */ jsxRuntime.jsx(
984
- DepthRow,
987
+ /* @__PURE__ */ jsxRuntime.jsx(
988
+ "div",
985
989
  {
986
- side: "ask",
987
- price: l.price,
988
- amount: l.amount,
989
- depthPct: (l.depth ?? l.amount) / maxAskDepth * 100,
990
- precision,
991
- amountPrecision
992
- },
993
- `ask-${idx}-${l.price}-${l.amount}`
994
- )) }),
990
+ className: "divide-y divide-white/5 overflow-y-auto",
991
+ style: { height: `${SECTION_HEIGHT}px`, scrollbarGutter: "stable" },
992
+ children: asks.map((l, idx) => /* @__PURE__ */ jsxRuntime.jsx(
993
+ DepthRow,
994
+ {
995
+ side: "ask",
996
+ price: l.price,
997
+ amount: l.amount,
998
+ depthPct: (l.depth ?? l.amount) / maxAskDepth * 100,
999
+ precision,
1000
+ amountPrecision
1001
+ },
1002
+ `ask-${idx}-${l.price}-${l.amount}`
1003
+ ))
1004
+ }
1005
+ ),
995
1006
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 grid grid-cols-2 items-center gap-3 bg-[#0b1a24] px-3 py-2", children: [
996
1007
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("text-lg font-semibold tabular-nums", midClass), children: [
997
1008
  "$",
@@ -1004,18 +1015,25 @@ var Orderbook = React5__namespace.forwardRef(
1004
1015
  ] }),
1005
1016
  /* @__PURE__ */ jsxRuntime.jsx("div", {})
1006
1017
  ] }),
1007
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-white/5", children: bids.map((l, idx) => /* @__PURE__ */ jsxRuntime.jsx(
1008
- DepthRow,
1018
+ /* @__PURE__ */ jsxRuntime.jsx(
1019
+ "div",
1009
1020
  {
1010
- side: "bid",
1011
- price: l.price,
1012
- amount: l.amount,
1013
- depthPct: (l.depth ?? l.amount) / maxBidDepth * 100,
1014
- precision,
1015
- amountPrecision
1016
- },
1017
- `bid-${idx}-${l.price}-${l.amount}`
1018
- )) })
1021
+ className: "divide-y divide-white/5 overflow-y-auto",
1022
+ style: { height: `${SECTION_HEIGHT}px`, scrollbarGutter: "stable" },
1023
+ children: bids.map((l, idx) => /* @__PURE__ */ jsxRuntime.jsx(
1024
+ DepthRow,
1025
+ {
1026
+ side: "bid",
1027
+ price: l.price,
1028
+ amount: l.amount,
1029
+ depthPct: (l.depth ?? l.amount) / maxBidDepth * 100,
1030
+ precision,
1031
+ amountPrecision
1032
+ },
1033
+ `bid-${idx}-${l.price}-${l.amount}`
1034
+ ))
1035
+ }
1036
+ )
1019
1037
  ] })
1020
1038
  ] })
1021
1039
  ]
@@ -1117,132 +1135,208 @@ var PropertyTour = React5__namespace.forwardRef(
1117
1135
  }
1118
1136
  );
1119
1137
  PropertyTour.displayName = "PropertyTour";
1120
- var TYPE_META = {
1138
+ var ITEMS_PER_PAGE = 4;
1139
+ var ROW_HEIGHT_PX = 86;
1140
+ var ensureAnimationsInjected = () => {
1141
+ if (typeof document === "undefined") return;
1142
+ if (document.getElementById("property-news-updates-animations")) return;
1143
+ const style = document.createElement("style");
1144
+ style.id = "property-news-updates-animations";
1145
+ style.textContent = `
1146
+ @keyframes propertyNewsPulse {
1147
+ 0% { opacity: 0.6; transform: scale(0.9); }
1148
+ 50% { opacity: 1; transform: scale(1); }
1149
+ 100% { opacity: 0.6; transform: scale(0.9); }
1150
+ }
1151
+
1152
+ @keyframes propertyNewsSlideIn {
1153
+ from { transform: translateY(-6px); opacity: 0; }
1154
+ to { transform: translateY(0); opacity: 1; }
1155
+ }
1156
+ `;
1157
+ document.head.appendChild(style);
1158
+ };
1159
+ var categoryStyles = {
1121
1160
  property: {
1122
1161
  label: "Property Update",
1123
- accent: "#f8d12f",
1124
- pillBg: "rgba(248, 209, 47, 0.12)",
1125
- pillBorder: "rgba(248, 209, 47, 0.45)",
1126
- pillColor: "#f8d12f",
1127
- icon: lucideReact.Building2
1162
+ backgroundColor: "rgba(14, 203, 129, 0.15)",
1163
+ borderColor: "rgba(14, 203, 129, 0.45)",
1164
+ color: "#0ecb81"
1128
1165
  },
1129
1166
  market: {
1130
- label: "Market Insight",
1131
- accent: "#0ecb81",
1132
- pillBg: "rgba(14, 203, 129, 0.12)",
1133
- pillBorder: "rgba(14, 203, 129, 0.45)",
1134
- pillColor: "#0ecb81",
1135
- icon: lucideReact.LineChart
1136
- },
1137
- portfolio: {
1138
- label: "Portfolio Signal",
1139
- accent: "#6c8cff",
1140
- pillBg: "rgba(108, 140, 255, 0.12)",
1141
- pillBorder: "rgba(108, 140, 255, 0.45)",
1142
- pillColor: "#c9d5ff",
1143
- icon: lucideReact.Sparkles
1167
+ label: "Market News",
1168
+ backgroundColor: "rgba(248, 209, 47, 0.15)",
1169
+ borderColor: "rgba(248, 209, 47, 0.45)",
1170
+ color: "#f8d12f"
1144
1171
  }
1145
1172
  };
1146
- var getTypeMeta = (type) => {
1147
- if (!type) return TYPE_META.property;
1148
- const key = type;
1149
- if (key in TYPE_META) return TYPE_META[key];
1150
- return {
1151
- label: type.toString(),
1152
- accent: "#f5f5f5",
1153
- pillBg: "rgba(255,255,255,0.08)",
1154
- pillBorder: "rgba(255,255,255,0.25)",
1155
- pillColor: "#ffffff",
1156
- icon: lucideReact.Newspaper
1157
- };
1158
- };
1159
1173
  var formatDate = (value) => {
1160
- const date = typeof value === "string" ? new Date(value) : value;
1161
- if (Number.isNaN(date.getTime())) return typeof value === "string" ? value : "";
1162
- return date.toLocaleDateString(void 0, {
1174
+ if (typeof value === "string") return value;
1175
+ if (!(value instanceof Date) || Number.isNaN(value.getTime())) return "";
1176
+ return value.toLocaleDateString(void 0, {
1163
1177
  month: "short",
1164
1178
  day: "numeric",
1165
1179
  year: "numeric"
1166
1180
  });
1167
1181
  };
1168
1182
  var PropertyNewsUpdates = React5__namespace.forwardRef(
1169
- ({ className, heading = "Latest property intelligence", subheading, items, emptyState, ...props }, ref) => {
1183
+ ({
1184
+ className,
1185
+ heading = "Property News & Headlines",
1186
+ subheading,
1187
+ items,
1188
+ emptyState,
1189
+ highlightFirst = true,
1190
+ ...props
1191
+ }, ref) => {
1192
+ const [page, setPage] = React5__namespace.useState(0);
1193
+ React5__namespace.useEffect(() => {
1194
+ ensureAnimationsInjected();
1195
+ }, []);
1170
1196
  const hasItems = Array.isArray(items) && items.length > 0;
1197
+ const totalPages = React5__namespace.useMemo(() => hasItems ? Math.max(1, Math.ceil(items.length / ITEMS_PER_PAGE)) : 1, [hasItems, items.length]);
1198
+ React5__namespace.useEffect(() => {
1199
+ setPage((prev) => Math.min(prev, totalPages - 1));
1200
+ }, [totalPages]);
1201
+ const paginatedItems = hasItems ? items.slice(page * ITEMS_PER_PAGE, page * ITEMS_PER_PAGE + ITEMS_PER_PAGE) : [];
1171
1202
  return /* @__PURE__ */ jsxRuntime.jsxs(
1172
1203
  "div",
1173
1204
  {
1174
1205
  ref,
1175
1206
  className: cn(
1176
- "w-full max-w-[480px] rounded-[20px] border border-white/10 bg-gradient-to-br from-[#06090f] via-[#0f1621] to-[#111b2a] p-6 text-white shadow-[0_30px_80px_rgba(3,7,18,0.65)] backdrop-blur-xl",
1207
+ "w-full max-w-[840px] rounded-2xl border border-white/10 bg-[#0b0e10] p-5 text-white shadow-[0_20px_40px_rgba(0,0,0,0.35)]",
1177
1208
  className
1178
1209
  ),
1179
1210
  ...props,
1180
1211
  children: [
1181
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
1182
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[0.85rem] uppercase tracking-[0.25em] text-white/50", children: heading }),
1183
- subheading ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-white/60", children: subheading }) : null
1184
- ] }),
1185
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-5 flex flex-col gap-4", children: hasItems ? items.map((item, index) => {
1186
- const meta = getTypeMeta(item.type);
1187
- const Icon = meta.icon;
1188
- const key = item.id ?? `${item.title}-${index}`;
1189
- const content = /* @__PURE__ */ jsxRuntime.jsxs(
1212
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1213
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1214
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg font-semibold text-white", children: heading }),
1215
+ subheading ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-white/60", children: subheading }) : null
1216
+ ] }),
1217
+ /* @__PURE__ */ jsxRuntime.jsxs(
1190
1218
  "div",
1191
1219
  {
1192
- className: "relative flex w-full gap-4 rounded-2xl border border-white/5 bg-white/[0.04] p-4 transition hover:border-white/15 hover:bg-white/[0.06]",
1220
+ className: "inline-flex items-center font-semibold uppercase text-emerald-300",
1221
+ style: { gap: "0.35rem", fontSize: "0.8rem", letterSpacing: "0.15em" },
1193
1222
  children: [
1194
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center", children: [
1195
- /* @__PURE__ */ jsxRuntime.jsx(
1196
- "div",
1197
- {
1198
- className: "flex h-12 w-12 items-center justify-center rounded-full border text-white",
1199
- style: {
1200
- background: meta.pillBg,
1201
- borderColor: meta.pillBorder,
1202
- color: meta.pillColor
1203
- },
1204
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-5 w-5" })
1205
- }
1206
- ),
1207
- index !== items.length - 1 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 h-full w-px flex-1 bg-white/10" }) : null
1208
- ] }),
1209
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col", children: [
1210
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
1211
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.7rem] font-semibold uppercase tracking-[0.3em]", style: { color: meta.accent }, children: meta.label }),
1212
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center text-xs text-white/60", children: [
1213
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CalendarDays, { className: "mr-1 h-3.5 w-3.5" }),
1214
- formatDate(item.date)
1215
- ] })
1216
- ] }),
1217
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-base font-semibold leading-snug", children: item.title }),
1218
- item.description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-white/65", children: item.description }) : null,
1219
- (item.href || item.onAction) && item.actionLabel ? /* @__PURE__ */ jsxRuntime.jsxs(
1220
- "button",
1221
- {
1222
- type: "button",
1223
- className: "group mt-3 inline-flex items-center gap-1 text-sm font-semibold text-white transition hover:text-white/80",
1224
- onClick: () => {
1225
- if (item.href) {
1226
- window?.open?.(item.href, "_blank", "noopener,noreferrer");
1227
- }
1228
- item.onAction?.(item);
1229
- },
1230
- children: [
1231
- item.actionLabel,
1232
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpRight, { className: "h-4 w-4 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5" })
1233
- ]
1223
+ /* @__PURE__ */ jsxRuntime.jsx(
1224
+ "span",
1225
+ {
1226
+ style: {
1227
+ display: "inline-block",
1228
+ width: "6px",
1229
+ height: "6px",
1230
+ borderRadius: "50%",
1231
+ backgroundColor: "#34d399",
1232
+ animation: "propertyNewsPulse 2s infinite"
1234
1233
  }
1235
- ) : null
1236
- ] })
1234
+ }
1235
+ ),
1236
+ "LIVE"
1237
1237
  ]
1238
- },
1239
- key
1240
- );
1241
- return content;
1242
- }) : emptyState ?? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center rounded-2xl border border-dashed border-white/20 px-6 py-10 text-center text-sm text-white/60", children: [
1243
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Newspaper, { className: "mb-3 h-8 w-8 text-white/40" }),
1244
- "No property news yet. Updates will land here as soon as we receive new intelligence."
1245
- ] }) })
1238
+ }
1239
+ )
1240
+ ] }),
1241
+ /* @__PURE__ */ jsxRuntime.jsx(
1242
+ "div",
1243
+ {
1244
+ className: "mt-4 flex flex-col gap-3",
1245
+ style: { minHeight: `${ITEMS_PER_PAGE * ROW_HEIGHT_PX}px` },
1246
+ children: hasItems ? paginatedItems.map((item, index) => {
1247
+ const absoluteIndex = page * ITEMS_PER_PAGE + index;
1248
+ const key = item.displayId ?? item.id ?? `${item.title}-${absoluteIndex}`;
1249
+ const styles = categoryStyles[item.type] ?? categoryStyles.market;
1250
+ const dateLabel = item.isNew ?? (highlightFirst && absoluteIndex === 0) ? "Just now" : typeof item.date === "string" && item.date.trim().length > 0 ? item.date : formatDate(item.date);
1251
+ const isHighlighted = item.isNew ?? (highlightFirst && absoluteIndex === 0);
1252
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1253
+ "div",
1254
+ {
1255
+ style: {
1256
+ padding: "0.75rem",
1257
+ borderRadius: "6px",
1258
+ backgroundColor: isHighlighted ? "rgba(14, 203, 129, 0.1)" : "transparent",
1259
+ border: "1px solid transparent",
1260
+ transition: "background-color 0.2s",
1261
+ animation: item.isNew ? "propertyNewsSlideIn 0.5s ease-out" : void 0
1262
+ },
1263
+ children: [
1264
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.9375rem", fontWeight: 500, marginBottom: "0.35rem" }, children: item.title }),
1265
+ /* @__PURE__ */ jsxRuntime.jsxs(
1266
+ "div",
1267
+ {
1268
+ style: {
1269
+ display: "flex",
1270
+ justifyContent: "space-between",
1271
+ alignItems: "center",
1272
+ fontSize: "0.75rem",
1273
+ color: "#b5b8c5"
1274
+ },
1275
+ children: [
1276
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: isHighlighted ? "#0ecb81" : "inherit" }, children: dateLabel }),
1277
+ /* @__PURE__ */ jsxRuntime.jsx(
1278
+ "span",
1279
+ {
1280
+ style: {
1281
+ padding: "0.25rem 0.6rem",
1282
+ borderRadius: "4px",
1283
+ border: `1px solid ${styles.borderColor}`,
1284
+ backgroundColor: styles.backgroundColor,
1285
+ color: styles.color,
1286
+ fontSize: "0.68rem",
1287
+ fontWeight: 600,
1288
+ textTransform: "uppercase"
1289
+ },
1290
+ children: item.type === "property" ? styles.label : "Market News"
1291
+ }
1292
+ )
1293
+ ]
1294
+ }
1295
+ )
1296
+ ]
1297
+ },
1298
+ key
1299
+ );
1300
+ }) : emptyState ?? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center rounded-2xl border border-dashed border-white/20 px-6 py-10 text-center text-sm text-white/60", children: [
1301
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Newspaper, { className: "mb-3 h-8 w-8 text-white/40" }),
1302
+ "No property news yet. Updates will land here as soon as we receive new intelligence."
1303
+ ] })
1304
+ }
1305
+ ),
1306
+ hasItems && totalPages > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 flex items-center justify-between text-xs text-white/60", children: [
1307
+ /* @__PURE__ */ jsxRuntime.jsx(
1308
+ "button",
1309
+ {
1310
+ type: "button",
1311
+ onClick: () => setPage((prev) => Math.max(0, prev - 1)),
1312
+ disabled: page === 0,
1313
+ className: cn(
1314
+ "rounded-full border border-white/15 px-3 py-1 uppercase tracking-[0.2em]",
1315
+ page === 0 ? "opacity-40 cursor-not-allowed" : "hover:border-white/40"
1316
+ ),
1317
+ children: "Prev"
1318
+ }
1319
+ ),
1320
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium text-white/70", children: [
1321
+ "Page ",
1322
+ page + 1,
1323
+ " / ",
1324
+ totalPages
1325
+ ] }),
1326
+ /* @__PURE__ */ jsxRuntime.jsx(
1327
+ "button",
1328
+ {
1329
+ type: "button",
1330
+ onClick: () => setPage((prev) => Math.min(totalPages - 1, prev + 1)),
1331
+ disabled: page >= totalPages - 1,
1332
+ className: cn(
1333
+ "rounded-full border border-white/15 px-3 py-1 uppercase tracking-[0.2em]",
1334
+ page >= totalPages - 1 ? "opacity-40 cursor-not-allowed" : "hover:border-white/40"
1335
+ ),
1336
+ children: "Next"
1337
+ }
1338
+ )
1339
+ ] }) : null
1246
1340
  ]
1247
1341
  }
1248
1342
  );
@@ -1317,12 +1411,13 @@ var PropertyCompareBar = React5__namespace.forwardRef(
1317
1411
  {
1318
1412
  ref,
1319
1413
  className: cn(
1320
- "flex w-full flex-col gap-3 rounded-[14px] border border-[#1a1f2b] bg-[#0f131c] px-4 py-3 text-white shadow-[0_18px_45px_rgba(0,0,0,0.55)] md:flex-row md:items-center md:gap-4",
1414
+ "flex w-full flex-col gap-3 border border-white/10 px-4 py-3 text-white shadow-[0_18px_40px_rgba(0,0,0,0.55)] md:flex-row md:items-center md:justify-between md:gap-4",
1321
1415
  className
1322
1416
  ),
1417
+ style: { borderRadius: "16px" },
1323
1418
  ...props,
1324
1419
  children: [
1325
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1", ref: dropdownRef, children: [
1420
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-auto", ref: dropdownRef, children: [
1326
1421
  /* @__PURE__ */ jsxRuntime.jsxs(
1327
1422
  "button",
1328
1423
  {
@@ -1330,7 +1425,7 @@ var PropertyCompareBar = React5__namespace.forwardRef(
1330
1425
  disabled: !hasAddresses,
1331
1426
  onClick: () => setIsDropdownOpen((prev) => !prev),
1332
1427
  className: cn(
1333
- "flex h-[42px] w-full items-center justify-between rounded-[10px] border border-[#202632] bg-[#181c27] px-4 text-left text-[15px] font-semibold text-white shadow-[inset_0_1px_0_rgba(255,255,255,0.05)] transition hover:bg-[#1d222e] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-accent,#e6c87e)]",
1428
+ "flex h-[42px] w-auto items-center gap-2 rounded-[12px] border border-transparent bg-transparent px-0 text-left text-[15px] font-semibold text-white transition hover:text-white/80 focus-visible:outline-none",
1334
1429
  !hasAddresses && "text-white/40"
1335
1430
  ),
1336
1431
  children: [
@@ -1349,7 +1444,7 @@ var PropertyCompareBar = React5__namespace.forwardRef(
1349
1444
  ]
1350
1445
  }
1351
1446
  ),
1352
- isDropdownOpen && hasAddresses ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 top-[calc(100%+8px)] z-20 w-full rounded-[12px] border border-[#1f2431] bg-[#161925] py-1 shadow-[0_25px_55px_rgba(0,0,0,0.65)] backdrop-blur-sm", children: normalizedAddresses.map((option) => {
1447
+ isDropdownOpen && hasAddresses ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 top-[calc(100%+8px)] z-20 w-full rounded-[12px] border border-white/10 py-1 shadow-[0_25px_55px_rgba(0,0,0,0.6)] bg-black", children: normalizedAddresses.map((option) => {
1353
1448
  const active = option.id === resolvedSelectedId;
1354
1449
  return /* @__PURE__ */ jsxRuntime.jsx(
1355
1450
  "button",
@@ -1371,7 +1466,7 @@ var PropertyCompareBar = React5__namespace.forwardRef(
1371
1466
  {
1372
1467
  variant: "accentOutline",
1373
1468
  size: "sm",
1374
- className: "flex w-full items-center justify-center gap-2 rounded-[10px] border-[var(--color-accent,#e6c87e)] bg-transparent px-4 py-2 text-[14px] font-semibold text-[var(--color-accent,#e6c87e)] shadow-[0_10px_25px_rgba(0,0,0,0.45)] transition hover:bg-[rgba(230,200,126,0.08)] md:w-max",
1469
+ className: "flex items-center justify-center gap-2 rounded-[10px] border-[var(--color-accent,#e6c87e)] bg-transparent px-4 py-2 text-[14px] font-semibold text-[var(--color-accent,#e6c87e)] transition hover:bg-[rgba(230,200,126,0.08)] md:ml-auto",
1375
1470
  onClick: onCompareClick,
1376
1471
  disabled: !hasAddresses,
1377
1472
  children: [
@@ -1386,152 +1481,263 @@ var PropertyCompareBar = React5__namespace.forwardRef(
1386
1481
  }
1387
1482
  );
1388
1483
  PropertyCompareBar.displayName = "PropertyCompareBar";
1389
- var clampPct = (pct) => Math.min(100, Math.max(0, pct));
1390
- var EditIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs(
1391
- "svg",
1392
- {
1393
- viewBox: "0 0 24 24",
1394
- fill: "none",
1395
- xmlns: "http://www.w3.org/2000/svg",
1396
- className,
1397
- children: [
1398
- /* @__PURE__ */ jsxRuntime.jsx(
1399
- "path",
1400
- {
1401
- d: "M12 20H21",
1402
- stroke: "currentColor",
1403
- strokeWidth: "2",
1404
- strokeLinecap: "round",
1405
- strokeLinejoin: "round"
1406
- }
1407
- ),
1408
- /* @__PURE__ */ jsxRuntime.jsx(
1409
- "path",
1410
- {
1411
- d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5Z",
1412
- stroke: "currentColor",
1413
- strokeWidth: "2",
1414
- strokeLinecap: "round",
1415
- strokeLinejoin: "round"
1416
- }
1417
- )
1418
- ]
1419
- }
1420
- );
1484
+ var TABS = [
1485
+ { id: "portfolio", label: "Portfolio" },
1486
+ { id: "openOrders", label: "Open Orders" },
1487
+ { id: "tradeHistory", label: "Trade History" },
1488
+ { id: "orderHistory", label: "Order History" }
1489
+ ];
1490
+ var panelStyle = {
1491
+ width: "100%",
1492
+ borderRadius: "16px",
1493
+ backgroundColor: "#111111",
1494
+ border: "1px solid rgba(255,255,255,0.08)",
1495
+ color: "#ffffff",
1496
+ display: "flex",
1497
+ flexDirection: "column"
1498
+ };
1499
+ var headerStyle = {
1500
+ padding: "1.25rem 1.5rem 0.5rem"
1501
+ };
1502
+ var titleStyle = {
1503
+ margin: 0,
1504
+ fontSize: "1.1rem",
1505
+ fontWeight: 600
1506
+ };
1507
+ var tabsRowStyle = {
1508
+ display: "flex",
1509
+ gap: "0.5rem",
1510
+ padding: "0 1.5rem",
1511
+ borderBottom: "1px solid rgba(255,255,255,0.08)"
1512
+ };
1513
+ var tabButtonBase = {
1514
+ background: "none",
1515
+ border: "none",
1516
+ color: "rgba(255,255,255,0.5)",
1517
+ fontSize: "0.9rem",
1518
+ fontWeight: 400,
1519
+ padding: "0.75rem 1rem",
1520
+ cursor: "pointer",
1521
+ position: "relative",
1522
+ transition: "color 0.2s ease"
1523
+ };
1524
+ var tabContentWrapper = {
1525
+ padding: "1.25rem 1.5rem 1.5rem",
1526
+ display: "flex",
1527
+ flexDirection: "column",
1528
+ gap: "0.75rem",
1529
+ flex: 1
1530
+ };
1531
+ var tableHeaderStyle = {
1532
+ display: "grid",
1533
+ gridTemplateColumns: "1.8fr 0.9fr 0.7fr 0.8fr 0.8fr 1fr",
1534
+ gap: "0.5rem",
1535
+ paddingBottom: "0.75rem",
1536
+ borderBottom: "1px solid rgba(255,255,255,0.1)",
1537
+ minWidth: "700px"
1538
+ };
1539
+ var tableHeaderCell = {
1540
+ fontSize: "0.7rem",
1541
+ fontWeight: 700,
1542
+ color: "#ffffff",
1543
+ textTransform: "uppercase",
1544
+ letterSpacing: "0.3px"
1545
+ };
1546
+ var emptyStateStyle = {
1547
+ flex: 1,
1548
+ display: "flex",
1549
+ alignItems: "center",
1550
+ justifyContent: "center",
1551
+ textAlign: "center",
1552
+ color: "rgba(255,255,255,0.6)",
1553
+ fontSize: "0.95rem",
1554
+ border: "1px dashed rgba(255,255,255,0.12)",
1555
+ borderRadius: "8px",
1556
+ minHeight: "220px"
1557
+ };
1558
+ var rowStyle = {
1559
+ display: "grid",
1560
+ gridTemplateColumns: "1.8fr 0.9fr 0.7fr 0.8fr 0.8fr 1fr",
1561
+ gap: "0.5rem",
1562
+ alignItems: "center",
1563
+ minWidth: "700px",
1564
+ padding: "0.75rem 0",
1565
+ borderBottom: "1px solid rgba(255,255,255,0.08)"
1566
+ };
1567
+ var formatCurrency = (value) => {
1568
+ if (value == null || Number.isNaN(value)) return "\u2014";
1569
+ return `$${value.toLocaleString(void 0, {
1570
+ minimumFractionDigits: 2,
1571
+ maximumFractionDigits: 2
1572
+ })}`;
1573
+ };
1574
+ var formatPercent = (value, fractionDigits = 2) => {
1575
+ if (value == null || Number.isNaN(value)) return "\u2014";
1576
+ return `${value.toFixed(fractionDigits)}%`;
1577
+ };
1421
1578
  var YourOrders = React5__namespace.forwardRef(
1422
- ({
1423
- className,
1424
- title = "Your Orders",
1425
- orders,
1426
- onEditPrice,
1427
- onEditAmount,
1428
- onCancel,
1429
- ...props
1430
- }, ref) => {
1431
- return /* @__PURE__ */ jsxRuntime.jsxs(
1432
- Card,
1433
- {
1434
- ref,
1435
- className: cn(
1436
- "w-full min-h-[301.52px] rounded-[12px] border border-white/10 bg-black/30 text-white backdrop-blur-md max-[1024px]:min-h-[250px] max-[480px]:min-h-[200px]",
1437
- className
1438
- ),
1439
- ...props,
1440
- children: [
1441
- /* @__PURE__ */ jsxRuntime.jsx(CardHeader, { className: "px-6 pb-2 pt-6", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "m-0 text-[1.1rem] font-semibold text-white", children: title }) }),
1442
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 pb-6 pt-2", children: [
1443
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-[1.2fr_0.8fr_1fr_1fr_1fr_1fr_1.2fr_0.8fr] border-b border-white/10 pb-2 text-[0.85rem] font-medium text-white/60", children: [
1444
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2", children: "Asset" }),
1445
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2", children: "Type" }),
1446
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2", children: "Date" }),
1447
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2", children: "Price" }),
1448
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2", children: "Amount" }),
1449
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2", children: "Total" }),
1450
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2", children: "Filled" }),
1451
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 text-center", children: "Actions" })
1452
- ] }),
1453
- orders.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-10 text-center text-sm text-white/50", children: "No orders" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: orders.map((order) => {
1454
- const sideClass = order.side === "buy" ? "text-[#0ecb81]" : "text-[#f6465d]";
1455
- const filled = clampPct(order.filledPercent ?? 0);
1456
- const isCancelled = order.status?.toUpperCase() === "CANCELLED";
1457
- const canEditPrice = Boolean(onEditPrice);
1458
- const canEditAmount = Boolean(onEditAmount);
1459
- const canCancel = Boolean(onCancel);
1460
- return /* @__PURE__ */ jsxRuntime.jsxs(
1461
- "div",
1462
- {
1463
- className: "grid grid-cols-[1.5fr_0.8fr_0.8fr_1fr_0.8fr_1fr_1fr_0.8fr] items-center border-b border-white/5 py-2 text-[0.9rem] transition-colors hover:bg-white/[0.03]",
1464
- children: [
1465
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate px-2 font-medium", children: order.asset }),
1466
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("px-2 font-medium uppercase", sideClass), children: order.side }),
1467
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 font-medium", children: order.date }),
1468
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-2 font-medium", children: [
1469
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "tabular-nums", children: [
1470
- "$",
1471
- order.price.toLocaleString()
1472
- ] }),
1473
- /* @__PURE__ */ jsxRuntime.jsx(
1474
- "button",
1475
- {
1476
- type: "button",
1477
- onClick: canEditPrice ? () => onEditPrice?.(order) : void 0,
1478
- className: cn(
1479
- "ml-1 inline-flex items-center justify-center text-white/60 transition-colors",
1480
- canEditPrice ? "cursor-pointer hover:text-[#C9A227] active:translate-y-px" : "cursor-default"
1481
- ),
1482
- "aria-label": "Edit price",
1483
- children: /* @__PURE__ */ jsxRuntime.jsx(EditIcon, { className: "h-[14px] w-[14px]" })
1484
- }
1485
- )
1486
- ] }),
1487
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-2 font-medium", children: [
1488
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: order.amount.toLocaleString() }),
1489
- /* @__PURE__ */ jsxRuntime.jsx(
1490
- "button",
1579
+ ({ className, title = "Portfolio Holdings (Demo)", orders, ...props }, ref) => {
1580
+ const [activeTab, setActiveTab] = React5__namespace.useState("portfolio");
1581
+ const hasOrders = Array.isArray(orders) && orders.length > 0;
1582
+ const renderPortfolio = () => {
1583
+ if (!hasOrders) {
1584
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: emptyStateStyle, children: "No holdings yet. Start trading to build your portfolio." });
1585
+ }
1586
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1587
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: tableHeaderStyle, children: [
1588
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tableHeaderCell, children: "Property" }),
1589
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tableHeaderCell, children: "Value" }),
1590
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tableHeaderCell, children: "Holding" }),
1591
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tableHeaderCell, children: "% of Property" }),
1592
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tableHeaderCell, children: "Avg Price" }),
1593
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tableHeaderCell, children: "P&L" })
1594
+ ] }),
1595
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "0.65rem" }, children: orders.map((order) => {
1596
+ const propertyName = order.asset;
1597
+ const holding = order.holdingLabel ?? `${order.amount.toLocaleString(void 0, {
1598
+ minimumFractionDigits: 0,
1599
+ maximumFractionDigits: 2
1600
+ })} ${propertyName}`;
1601
+ const value = order.value ?? order.total;
1602
+ const portfolioShare = order.portfolioSharePercent != null ? `${order.portfolioSharePercent.toFixed(1)}% of portfolio` : void 0;
1603
+ const propertyPercent = order.propertyPercent;
1604
+ const avgPrice = order.avgEntryPrice ?? order.price;
1605
+ const pnlValue = order.pnlValue;
1606
+ const pnlPercent = order.pnlPercent;
1607
+ const pnlPositive = pnlValue != null ? pnlValue >= 0 : (pnlPercent ?? 0) >= 0;
1608
+ const currentPrice = order.currentPrice ?? order.price;
1609
+ const priceChangePercent = order.priceChangePercent ?? order.pnlPercent;
1610
+ const priceChangePositive = order.priceChangePositive ?? (priceChangePercent != null ? priceChangePercent >= 0 : void 0);
1611
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: rowStyle, children: [
1612
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "1rem", alignItems: "center" }, children: [
1613
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.15rem" }, children: [
1614
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "0.9rem", fontWeight: 500 }, children: propertyName }),
1615
+ portfolioShare ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "0.72rem", color: "rgba(255,255,255,0.5)" }, children: portfolioShare }) : null
1616
+ ] }),
1617
+ currentPrice != null ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1618
+ /* @__PURE__ */ jsxRuntime.jsxs(
1619
+ "div",
1620
+ {
1621
+ style: {
1622
+ fontSize: "0.9rem",
1623
+ color: "var(--color-text, #ffffff)",
1624
+ display: "flex",
1625
+ alignItems: "center",
1626
+ gap: "0.25rem"
1627
+ },
1628
+ children: [
1629
+ priceChangePositive != null ? /* @__PURE__ */ jsxRuntime.jsx(
1630
+ "span",
1491
1631
  {
1492
- type: "button",
1493
- onClick: canEditAmount ? () => onEditAmount?.(order) : void 0,
1494
- className: cn(
1495
- "ml-1 inline-flex items-center justify-center text-white/60 transition-colors",
1496
- canEditAmount ? "cursor-pointer hover:text-[#C9A227] active:translate-y-px" : "cursor-default"
1497
- ),
1498
- "aria-label": "Edit amount",
1499
- children: /* @__PURE__ */ jsxRuntime.jsx(EditIcon, { className: "h-[14px] w-[14px]" })
1632
+ style: {
1633
+ color: priceChangePositive ? "#0ecb81" : "#f6465d",
1634
+ fontSize: "0.7em"
1635
+ },
1636
+ children: priceChangePositive ? "\u25B2" : "\u25BC"
1500
1637
  }
1501
- )
1502
- ] }),
1503
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-2 font-medium tabular-nums", children: [
1504
- "$",
1505
- order.total.toLocaleString()
1506
- ] }),
1507
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-2", children: [
1508
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[0.875rem] font-medium tabular-nums", children: [
1509
- filled.toFixed(0),
1510
- "%"
1511
- ] }),
1512
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 h-1 w-4/5 overflow-hidden rounded-sm bg-white/10", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full rounded-sm bg-[#C9A227]", style: { width: `${filled}%` } }) })
1513
- ] }),
1514
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 text-center", children: isCancelled ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex items-center justify-center rounded border border-white/20 px-3 py-1 text-[0.78rem] uppercase tracking-wide text-[#f6465d]", children: "Cancelled" }) : /* @__PURE__ */ jsxRuntime.jsx(
1515
- "button",
1516
- {
1517
- type: "button",
1518
- onClick: canCancel ? () => onCancel?.(order) : void 0,
1519
- className: cn(
1520
- "rounded border border-white/40 px-3 py-1 text-[0.8rem] text-white/70 transition-colors",
1521
- canCancel ? "cursor-pointer hover:bg-white/10 active:text-[#f6465d] active:border-[#f6465d] active:translate-y-px" : "cursor-default"
1522
- ),
1523
- children: "Cancel"
1524
- }
1525
- ) })
1526
- ]
1638
+ ) : null,
1639
+ formatCurrency(currentPrice)
1640
+ ]
1641
+ }
1642
+ ),
1643
+ priceChangePercent != null ? /* @__PURE__ */ jsxRuntime.jsxs(
1644
+ "div",
1645
+ {
1646
+ style: {
1647
+ fontSize: "0.7rem",
1648
+ color: priceChangePositive ? "#0ecb81" : "#f6465d"
1649
+ },
1650
+ children: [
1651
+ "(",
1652
+ priceChangePositive ? "+" : "",
1653
+ priceChangePercent.toFixed(2),
1654
+ "%)"
1655
+ ]
1656
+ }
1657
+ ) : null
1658
+ ] }) : null
1659
+ ] }),
1660
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "0.9rem", fontWeight: 500, color: "#D4AF37" }, children: formatCurrency(value) }),
1661
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "0.9rem", color: "rgba(255,255,255,0.92)" }, children: holding }),
1662
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "0.9rem", color: "rgba(255,255,255,0.8)" }, children: formatPercent(propertyPercent, 3) }),
1663
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "0.9rem", color: "rgba(255,255,255,0.9)" }, children: formatCurrency(avgPrice) }),
1664
+ /* @__PURE__ */ jsxRuntime.jsxs(
1665
+ "div",
1666
+ {
1667
+ style: {
1668
+ fontSize: "0.9rem",
1669
+ fontWeight: 500,
1670
+ color: pnlPositive ? "#0ecb81" : "#f6465d"
1527
1671
  },
1528
- order.id
1529
- );
1530
- }) })
1531
- ] })
1532
- ]
1533
- }
1534
- );
1672
+ children: [
1673
+ pnlValue != null ? `${pnlPositive ? "+" : "-"}${formatCurrency(Math.abs(pnlValue))}` : formatCurrency(pnlValue),
1674
+ pnlPercent != null ? /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { marginLeft: "0.35rem", fontSize: "0.75rem", color: "rgba(255,255,255,0.6)" }, children: [
1675
+ "(",
1676
+ pnlPositive ? "+" : "",
1677
+ pnlPercent.toFixed(1),
1678
+ "%)"
1679
+ ] }) : null
1680
+ ]
1681
+ }
1682
+ )
1683
+ ] }, order.id);
1684
+ }) })
1685
+ ] });
1686
+ };
1687
+ const renderOtherTab = (label) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: emptyStateStyle, children: `No ${label.toLowerCase()} data available.` });
1688
+ let tabContent = null;
1689
+ switch (activeTab) {
1690
+ case "portfolio":
1691
+ tabContent = renderPortfolio();
1692
+ break;
1693
+ case "openOrders":
1694
+ tabContent = renderOtherTab("Open Orders");
1695
+ break;
1696
+ case "tradeHistory":
1697
+ tabContent = renderOtherTab("Trade History");
1698
+ break;
1699
+ case "orderHistory":
1700
+ tabContent = renderOtherTab("Order History");
1701
+ break;
1702
+ default:
1703
+ tabContent = null;
1704
+ }
1705
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, style: panelStyle, className, ...props, children: [
1706
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: headerStyle, children: /* @__PURE__ */ jsxRuntime.jsx("h3", { style: titleStyle, children: title }) }),
1707
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tabsRowStyle, children: TABS.map((tab) => {
1708
+ const isActive = activeTab === tab.id;
1709
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1710
+ "button",
1711
+ {
1712
+ type: "button",
1713
+ onClick: () => setActiveTab(tab.id),
1714
+ style: {
1715
+ ...tabButtonBase,
1716
+ color: isActive ? "#ffffff" : "rgba(255,255,255,0.55)",
1717
+ fontWeight: isActive ? 600 : 400
1718
+ },
1719
+ children: [
1720
+ tab.label,
1721
+ isActive ? /* @__PURE__ */ jsxRuntime.jsx(
1722
+ "div",
1723
+ {
1724
+ style: {
1725
+ position: "absolute",
1726
+ bottom: "-1px",
1727
+ left: 0,
1728
+ right: 0,
1729
+ height: "2px",
1730
+ background: "#f0b90b"
1731
+ }
1732
+ }
1733
+ ) : null
1734
+ ]
1735
+ },
1736
+ tab.id
1737
+ );
1738
+ }) }),
1739
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tabContentWrapper, children: tabContent })
1740
+ ] });
1535
1741
  }
1536
1742
  );
1537
1743
  YourOrders.displayName = "YourOrders";
@@ -1741,7 +1947,10 @@ var PropertyHeroHeader = React5__namespace.forwardRef(
1741
1947
  ...props
1742
1948
  }, ref) => {
1743
1949
  const isPositive = changePercent == null ? void 0 : changePercent >= 0;
1744
- const tradeHoverColor = "var(--color-accent-hover, #f8d12f)";
1950
+ const accentColor = "#e6c87e";
1951
+ const tradeHoverColor = "#f5dd9a";
1952
+ const [isTradeInteracting, setIsTradeInteracting] = React5__namespace.useState(false);
1953
+ const [isOfferInteracting, setIsOfferInteracting] = React5__namespace.useState(false);
1745
1954
  return /* @__PURE__ */ jsxRuntime.jsxs(
1746
1955
  "div",
1747
1956
  {
@@ -1827,20 +2036,30 @@ var PropertyHeroHeader = React5__namespace.forwardRef(
1827
2036
  propertyTypeLabel == null ? null : /* @__PURE__ */ jsxRuntime.jsx("div", { children: propertyTypeLabel })
1828
2037
  ] })
1829
2038
  ] }),
1830
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 max-[768px]:w-full max-[768px]:flex-row max-[768px]:justify-between max-[768px]:gap-2 max-[480px]:gap-2", children: [
2039
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-3 max-[768px]:w-full max-[768px]:justify-between max-[768px]:gap-2 max-[480px]:gap-2", children: [
1831
2040
  /* @__PURE__ */ jsxRuntime.jsx(
1832
2041
  "button",
1833
2042
  {
1834
2043
  type: "button",
1835
2044
  onClick: onTrade,
1836
- className: "rounded border border-transparent px-6 py-3 font-semibold transition-all hover:-translate-y-0.5 hover:shadow-[0_4px_8px_rgba(0,0,0,0.2)] active:translate-y-0 active:shadow-[0_2px_4px_rgba(0,0,0,0.1)] max-[768px]:flex-1 max-[768px]:px-5 max-[768px]:py-2.5 max-[768px]:text-[0.95rem] max-[480px]:px-4 max-[480px]:py-2 max-[480px]:text-[0.9rem]",
1837
- style: { backgroundColor: "var(--color-accent, #e6c87e)", color: "black" },
1838
- onMouseEnter: (e) => {
1839
- e.currentTarget.style.backgroundColor = tradeHoverColor;
1840
- },
1841
- onMouseLeave: (e) => {
1842
- e.currentTarget.style.backgroundColor = "var(--color-accent, #e6c87e)";
2045
+ className: "flex items-center justify-center rounded border font-semibold transition-all duration-200 hover:-translate-y-0.5 hover:shadow-[0_4px_8px_rgba(0,0,0,0.2)] active:translate-y-0 active:shadow-[0_2px_4px_rgba(0,0,0,0.1)] text-[0.95rem] max-[480px]:text-[0.9rem]",
2046
+ style: {
2047
+ backgroundColor: isTradeInteracting ? tradeHoverColor : accentColor,
2048
+ color: "black",
2049
+ width: "88.06px",
2050
+ height: "43px",
2051
+ minWidth: "88.06px",
2052
+ borderColor: isTradeInteracting ? accentColor : "transparent",
2053
+ boxShadow: isTradeInteracting ? `0 0 0 2px rgba(0,0,0,0.4), 0 0 0 4px ${accentColor}` : "none"
1843
2054
  },
2055
+ onMouseEnter: () => setIsTradeInteracting(true),
2056
+ onMouseLeave: () => setIsTradeInteracting(false),
2057
+ onMouseDown: () => setIsTradeInteracting(true),
2058
+ onMouseUp: () => setIsTradeInteracting(false),
2059
+ onFocus: () => setIsTradeInteracting(true),
2060
+ onBlur: () => setIsTradeInteracting(false),
2061
+ onTouchStart: () => setIsTradeInteracting(true),
2062
+ onTouchEnd: () => setIsTradeInteracting(false),
1844
2063
  children: "Trade"
1845
2064
  }
1846
2065
  ),
@@ -1849,18 +2068,24 @@ var PropertyHeroHeader = React5__namespace.forwardRef(
1849
2068
  {
1850
2069
  type: "button",
1851
2070
  onClick: onMakeOffer,
1852
- className: "rounded border px-6 py-3 font-semibold transition-all hover:-translate-y-0.5 hover:shadow-[0_4px_8px_rgba(0,0,0,0.2)] active:translate-y-0 active:shadow-[0_2px_4px_rgba(0,0,0,0.1)] max-[768px]:flex-1 max-[768px]:px-5 max-[768px]:py-2.5 max-[768px]:text-[0.95rem] max-[480px]:px-4 max-[480px]:py-2 max-[480px]:text-[0.9rem]",
2071
+ className: "flex items-center justify-center rounded border font-semibold transition-all duration-200 hover:-translate-y-0.5 hover:shadow-[0_4px_8px_rgba(0,0,0,0.2)] active:translate-y-0 active:shadow-[0_2px_4px_rgba(0,0,0,0.1)] text-[0.95rem] max-[480px]:text-[0.9rem]",
1853
2072
  style: {
1854
- backgroundColor: "transparent",
1855
- borderColor: "var(--color-accent, #e6c87e)",
1856
- color: "var(--color-accent, #e6c87e)"
1857
- },
1858
- onMouseEnter: (e) => {
1859
- e.currentTarget.style.backgroundColor = "rgba(240, 185, 11, 0.1)";
1860
- },
1861
- onMouseLeave: (e) => {
1862
- e.currentTarget.style.backgroundColor = "transparent";
2073
+ backgroundColor: isOfferInteracting ? accentColor : "transparent",
2074
+ borderColor: accentColor,
2075
+ color: isOfferInteracting ? "black" : accentColor,
2076
+ width: "127.14px",
2077
+ height: "43px",
2078
+ minWidth: "127.14px",
2079
+ boxShadow: isOfferInteracting ? `0 0 0 2px rgba(0,0,0,0.4), 0 0 0 4px ${accentColor}` : "none"
1863
2080
  },
2081
+ onMouseEnter: () => setIsOfferInteracting(true),
2082
+ onMouseLeave: () => setIsOfferInteracting(false),
2083
+ onMouseDown: () => setIsOfferInteracting(true),
2084
+ onMouseUp: () => setIsOfferInteracting(false),
2085
+ onFocus: () => setIsOfferInteracting(true),
2086
+ onBlur: () => setIsOfferInteracting(false),
2087
+ onTouchStart: () => setIsOfferInteracting(true),
2088
+ onTouchEnd: () => setIsOfferInteracting(false),
1864
2089
  children: "Make Offer"
1865
2090
  }
1866
2091
  )