@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.mjs CHANGED
@@ -4,7 +4,7 @@ import { cva } from 'class-variance-authority';
4
4
  import { clsx } from 'clsx';
5
5
  import { twMerge } from 'tailwind-merge';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
- import { CalendarDays, ArrowUpRight, Newspaper, BedDouble, Bath, CarFront, Building2, Sparkles, LineChart } from 'lucide-react';
7
+ import { Newspaper, BedDouble, Bath, CarFront } from 'lucide-react';
8
8
  import { createChart } from 'lightweight-charts';
9
9
 
10
10
  // src/components/button.tsx
@@ -146,8 +146,8 @@ var PortfolioSummary = React5.forwardRef(
146
146
  onResetAccount,
147
147
  positionsHeading = "Current Positions",
148
148
  emptyPositionsText = "No positions yet. Start trading to build your portfolio!",
149
- formatCurrency = defaultFormatCurrency,
150
- formatPercent = defaultFormatPercent,
149
+ formatCurrency: formatCurrency2 = defaultFormatCurrency,
150
+ formatPercent: formatPercent2 = defaultFormatPercent,
151
151
  formatSignedCurrency = defaultFormatSignedCurrency,
152
152
  className,
153
153
  children,
@@ -184,15 +184,15 @@ var PortfolioSummary = React5.forwardRef(
184
184
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
185
185
  /* @__PURE__ */ jsxs("div", { children: [
186
186
  /* @__PURE__ */ jsx("p", { className: "text-xs uppercase tracking-[0.5px] text-white/50", children: "Available Cash" }),
187
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-[1.25rem] font-semibold text-white", children: formatCurrency(availableCash) })
187
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-[1.25rem] font-semibold text-white", children: formatCurrency2(availableCash) })
188
188
  ] }),
189
189
  /* @__PURE__ */ jsxs("div", { children: [
190
190
  /* @__PURE__ */ jsx("p", { className: "text-xs uppercase tracking-[0.5px] text-white/50", children: "Portfolio Value" }),
191
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-[1.25rem] font-semibold text-white", children: formatCurrency(portfolioValue) })
191
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-[1.25rem] font-semibold text-white", children: formatCurrency2(portfolioValue) })
192
192
  ] }),
193
193
  /* @__PURE__ */ jsxs("div", { children: [
194
194
  /* @__PURE__ */ jsx("p", { className: "text-xs uppercase tracking-[0.5px] text-white/50", children: "Total Return" }),
195
- /* @__PURE__ */ jsx("p", { className: cn("mt-1 text-[1.25rem] font-semibold", totalReturnClassName), children: formatPercent(totalReturnPercent) })
195
+ /* @__PURE__ */ jsx("p", { className: cn("mt-1 text-[1.25rem] font-semibold", totalReturnClassName), children: formatPercent2(totalReturnPercent) })
196
196
  ] }),
197
197
  /* @__PURE__ */ jsxs("div", { children: [
198
198
  /* @__PURE__ */ jsx("p", { className: "text-xs uppercase tracking-[0.5px] text-white/50", children: "Unrealized P&L" }),
@@ -282,6 +282,7 @@ function HousePositionSlider({
282
282
  pendingOrders = [],
283
283
  defaultOrderType = "market",
284
284
  orderbook,
285
+ ownershipPercentOverride,
285
286
  onConfirmOrder,
286
287
  className,
287
288
  ...props
@@ -292,6 +293,7 @@ function HousePositionSlider({
292
293
  const [deltaTokensBuy, setDeltaTokensBuy] = React5.useState(0);
293
294
  const [deltaTokensSell, setDeltaTokensSell] = React5.useState(0);
294
295
  const [isDragging, setIsDragging] = React5.useState(false);
296
+ const [visualTargetPct, setVisualTargetPct] = React5.useState(null);
295
297
  const [orderType, setOrderType] = React5.useState(defaultOrderType);
296
298
  const [limitPrice, setLimitPrice] = React5.useState(currentPrice);
297
299
  const [limitPriceInput, setLimitPriceInput] = React5.useState(currentPrice.toFixed(2));
@@ -313,9 +315,8 @@ function HousePositionSlider({
313
315
  const effectiveAvailableCash = Math.max(0, availableCash - pendingBuyValue);
314
316
  const effectiveTokensHeld = Math.max(0, tokensHeld - pendingSellTokens);
315
317
  const holdingsValue = tokensHeld * effectivePrice;
316
- const sliderHoldingsValue = effectiveTokensHeld * effectivePrice;
317
- const sliderTotalCapacity = sliderHoldingsValue + effectiveAvailableCash;
318
- const baselinePct = sliderTotalCapacity <= 0 ? 0 : sliderHoldingsValue / sliderTotalCapacity * 100;
318
+ const safeTotalTokens = totalTokens > 0 ? totalTokens : 1;
319
+ const baselineOwnershipActual = clamp(effectiveTokensHeld / safeTotalTokens * 100, 0, 100);
319
320
  let deltaTokens = 0;
320
321
  let deltaValue = 0;
321
322
  let marketAvgPrice = null;
@@ -361,29 +362,21 @@ function HousePositionSlider({
361
362
  }
362
363
  targetTokens = tokensHeld + deltaTokens;
363
364
  targetValue = targetTokens * effectivePrice;
364
- const plannedDeltaValue = (() => {
365
- if (orderMode === "buy") {
366
- if (buyTrackingMode === "dollars") {
367
- const notional = Math.min(Math.max(0, deltaDollars), effectiveAvailableCash);
368
- return notional;
369
- }
370
- const tokensPlanned = Math.max(0, deltaTokensBuy);
371
- const referencePrice = orderType === "market" ? currentPrice || limitPriceSafe : limitPriceSafe;
372
- return Math.min(tokensPlanned * referencePrice, effectiveAvailableCash);
373
- }
374
- if (orderMode === "sell") {
375
- const tokensToSell = Math.abs(Math.min(0, deltaTokensSell));
376
- const sellValue = tokensToSell * effectivePrice;
377
- return -Math.min(sellValue, sliderHoldingsValue);
378
- }
379
- return 0;
380
- })();
381
- const sliderTargetValue = clamp(sliderHoldingsValue + plannedDeltaValue, 0, sliderTotalCapacity);
382
- const targetPct = sliderTotalCapacity <= 0 ? 0 : sliderTargetValue / sliderTotalCapacity * 100;
365
+ const sliderTargetTokens = clamp(effectiveTokensHeld + deltaTokens, 0, safeTotalTokens);
366
+ const normalizedTargetPct = sliderTargetTokens / safeTotalTokens * 100;
383
367
  const isIncrease = orderMode === "buy";
384
368
  const hasChange = orderMode !== "none" && (Math.abs(deltaTokens) > 1e-3 || Math.abs(deltaValue) > 0.01);
385
- const currentOwnership = totalTokens <= 0 ? 0 : tokensHeld / totalTokens * 100;
386
- const targetOwnership = totalTokens <= 0 ? 0 : targetTokens / totalTokens * 100;
369
+ const currentOwnership = totalTokens <= 0 ? 0 : clamp(tokensHeld / totalTokens * 100, 0, 100);
370
+ const targetOwnership = totalTokens <= 0 ? 0 : clamp(targetTokens / totalTokens * 100, 0, 100);
371
+ const ownershipOverrideValue = typeof ownershipPercentOverride === "number" && Number.isFinite(ownershipPercentOverride) ? clamp(ownershipPercentOverride, 0, 100) : null;
372
+ const ownershipShift = ownershipOverrideValue != null ? ownershipOverrideValue - baselineOwnershipActual : 0;
373
+ const baselinePct = clamp(ownershipOverrideValue ?? baselineOwnershipActual, 0, 100);
374
+ const impliedTargetPct = clamp(normalizedTargetPct + ownershipShift, 0, 100);
375
+ const displayTargetPct = visualTargetPct ?? impliedTargetPct;
376
+ const targetPct = displayTargetPct;
377
+ const displayCurrentOwnership = clamp(ownershipOverrideValue ?? currentOwnership, 0, 100);
378
+ const impliedDisplayTargetOwnership = clamp(targetOwnership + ownershipShift, 0, 100);
379
+ const displayTargetOwnership = visualTargetPct ?? impliedDisplayTargetOwnership;
387
380
  const estFeeTokens = Math.abs(deltaValue) * 5e-3 / (effectivePrice || 1);
388
381
  const resetOrder = React5.useCallback(() => {
389
382
  setOrderMode("none");
@@ -391,6 +384,7 @@ function HousePositionSlider({
391
384
  setDeltaDollars(0);
392
385
  setDeltaTokensBuy(0);
393
386
  setDeltaTokensSell(0);
387
+ setVisualTargetPct(null);
394
388
  }, []);
395
389
  const updateOrderFromTargetValue = React5.useCallback(
396
390
  (newTargetValue) => {
@@ -421,6 +415,7 @@ function HousePositionSlider({
421
415
  const nextOwnership = clamp(newOwnershipPercent, 0, 100);
422
416
  const newTargetTokens = nextOwnership / 100 * totalTokens;
423
417
  updateOrderFromTargetValue(newTargetTokens * effectivePrice);
418
+ setVisualTargetPct(nextOwnership);
424
419
  },
425
420
  [effectivePrice, totalTokens, updateOrderFromTargetValue]
426
421
  );
@@ -453,12 +448,14 @@ function HousePositionSlider({
453
448
  const magnitude = Math.min(Math.abs(normalized), 1);
454
449
  if (magnitude < 0.02) {
455
450
  resetOrder();
451
+ setVisualTargetPct(null);
456
452
  return;
457
453
  }
458
454
  if (normalized > 0) {
459
455
  const notional = clamp(magnitude * effectiveAvailableCash, 0, effectiveAvailableCash);
460
456
  if (notional <= 0) {
461
457
  resetOrder();
458
+ setVisualTargetPct(null);
462
459
  return;
463
460
  }
464
461
  setOrderMode("buy");
@@ -466,12 +463,14 @@ function HousePositionSlider({
466
463
  setDeltaDollars(notional);
467
464
  setDeltaTokensBuy(0);
468
465
  setDeltaTokensSell(0);
466
+ setVisualTargetPct(clamp(pct, 0, 100));
469
467
  return;
470
468
  }
471
469
  if (normalized < 0) {
472
470
  const tokensToSell = clamp(magnitude * effectiveTokensHeld, 0, effectiveTokensHeld);
473
471
  if (tokensToSell <= 0) {
474
472
  resetOrder();
473
+ setVisualTargetPct(null);
475
474
  return;
476
475
  }
477
476
  setOrderMode("sell");
@@ -479,9 +478,11 @@ function HousePositionSlider({
479
478
  setDeltaTokensSell(-tokensToSell);
480
479
  setDeltaDollars(0);
481
480
  setDeltaTokensBuy(0);
481
+ setVisualTargetPct(clamp(pct, 0, 100));
482
482
  return;
483
483
  }
484
484
  resetOrder();
485
+ setVisualTargetPct(null);
485
486
  },
486
487
  [effectiveAvailableCash, effectiveTokensHeld, resetOrder]
487
488
  );
@@ -644,19 +645,19 @@ function HousePositionSlider({
644
645
  " Ownership"
645
646
  ] }),
646
647
  /* @__PURE__ */ jsxs("span", { className: "text-white", children: [
647
- currentOwnership.toFixed(2),
648
+ displayCurrentOwnership.toFixed(2),
648
649
  "%",
649
650
  /* @__PURE__ */ jsx("span", { className: "mx-1.5 text-white/50", children: "\u2192" }),
650
651
  /* @__PURE__ */ jsx(
651
652
  "input",
652
653
  {
653
654
  type: "text",
654
- value: ownershipInput || targetOwnership.toFixed(2),
655
+ value: ownershipInput || displayTargetOwnership.toFixed(2),
655
656
  onChange: (e) => {
656
657
  const val = e.target.value;
657
658
  if (val === "" || /^[0-9]*\.?[0-9]*$/.test(val)) setOwnershipInput(val);
658
659
  },
659
- onFocus: () => setOwnershipInput(targetOwnership.toFixed(2)),
660
+ onFocus: () => setOwnershipInput(displayTargetOwnership.toFixed(2)),
660
661
  onBlur: () => {
661
662
  const num = Number.parseFloat(ownershipInput);
662
663
  if (Number.isFinite(num)) updateOrderFromOwnership(num);
@@ -667,7 +668,7 @@ function HousePositionSlider({
667
668
  },
668
669
  className: cn(
669
670
  "w-[70px] rounded-[4px] border bg-white/10 px-2 py-1 text-right font-semibold outline-none",
670
- 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]"
671
+ 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]"
671
672
  )
672
673
  }
673
674
  )
@@ -818,6 +819,9 @@ function DepthRow({
818
819
  ] });
819
820
  }
820
821
  var clamp2 = (value, min, max) => Math.min(max, Math.max(min, value));
822
+ var LEVEL_ROWS_VISIBLE = 6;
823
+ var DEPTH_ROW_HEIGHT_PX = 34;
824
+ var SECTION_HEIGHT = LEVEL_ROWS_VISIBLE * DEPTH_ROW_HEIGHT_PX;
821
825
  var Orderbook = React5.forwardRef(
822
826
  ({
823
827
  asks,
@@ -958,18 +962,25 @@ var Orderbook = React5.forwardRef(
958
962
  ] }, `${trade.type}-${trade.price}-${trade.amount}-${trade.time ?? i}`)) })
959
963
  }
960
964
  ) : /* @__PURE__ */ jsxs(Fragment, { children: [
961
- /* @__PURE__ */ jsx("div", { className: "divide-y divide-white/5", children: asks.map((l, idx) => /* @__PURE__ */ jsx(
962
- DepthRow,
965
+ /* @__PURE__ */ jsx(
966
+ "div",
963
967
  {
964
- side: "ask",
965
- price: l.price,
966
- amount: l.amount,
967
- depthPct: (l.depth ?? l.amount) / maxAskDepth * 100,
968
- precision,
969
- amountPrecision
970
- },
971
- `ask-${idx}-${l.price}-${l.amount}`
972
- )) }),
968
+ className: "divide-y divide-white/5 overflow-y-auto",
969
+ style: { height: `${SECTION_HEIGHT}px`, scrollbarGutter: "stable" },
970
+ children: asks.map((l, idx) => /* @__PURE__ */ jsx(
971
+ DepthRow,
972
+ {
973
+ side: "ask",
974
+ price: l.price,
975
+ amount: l.amount,
976
+ depthPct: (l.depth ?? l.amount) / maxAskDepth * 100,
977
+ precision,
978
+ amountPrecision
979
+ },
980
+ `ask-${idx}-${l.price}-${l.amount}`
981
+ ))
982
+ }
983
+ ),
973
984
  /* @__PURE__ */ jsxs("div", { className: "mt-2 grid grid-cols-2 items-center gap-3 bg-[#0b1a24] px-3 py-2", children: [
974
985
  /* @__PURE__ */ jsxs("div", { className: cn("text-lg font-semibold tabular-nums", midClass), children: [
975
986
  "$",
@@ -982,18 +993,25 @@ var Orderbook = React5.forwardRef(
982
993
  ] }),
983
994
  /* @__PURE__ */ jsx("div", {})
984
995
  ] }),
985
- /* @__PURE__ */ jsx("div", { className: "divide-y divide-white/5", children: bids.map((l, idx) => /* @__PURE__ */ jsx(
986
- DepthRow,
996
+ /* @__PURE__ */ jsx(
997
+ "div",
987
998
  {
988
- side: "bid",
989
- price: l.price,
990
- amount: l.amount,
991
- depthPct: (l.depth ?? l.amount) / maxBidDepth * 100,
992
- precision,
993
- amountPrecision
994
- },
995
- `bid-${idx}-${l.price}-${l.amount}`
996
- )) })
999
+ className: "divide-y divide-white/5 overflow-y-auto",
1000
+ style: { height: `${SECTION_HEIGHT}px`, scrollbarGutter: "stable" },
1001
+ children: bids.map((l, idx) => /* @__PURE__ */ jsx(
1002
+ DepthRow,
1003
+ {
1004
+ side: "bid",
1005
+ price: l.price,
1006
+ amount: l.amount,
1007
+ depthPct: (l.depth ?? l.amount) / maxBidDepth * 100,
1008
+ precision,
1009
+ amountPrecision
1010
+ },
1011
+ `bid-${idx}-${l.price}-${l.amount}`
1012
+ ))
1013
+ }
1014
+ )
997
1015
  ] })
998
1016
  ] })
999
1017
  ]
@@ -1095,132 +1113,208 @@ var PropertyTour = React5.forwardRef(
1095
1113
  }
1096
1114
  );
1097
1115
  PropertyTour.displayName = "PropertyTour";
1098
- var TYPE_META = {
1116
+ var ITEMS_PER_PAGE = 4;
1117
+ var ROW_HEIGHT_PX = 86;
1118
+ var ensureAnimationsInjected = () => {
1119
+ if (typeof document === "undefined") return;
1120
+ if (document.getElementById("property-news-updates-animations")) return;
1121
+ const style = document.createElement("style");
1122
+ style.id = "property-news-updates-animations";
1123
+ style.textContent = `
1124
+ @keyframes propertyNewsPulse {
1125
+ 0% { opacity: 0.6; transform: scale(0.9); }
1126
+ 50% { opacity: 1; transform: scale(1); }
1127
+ 100% { opacity: 0.6; transform: scale(0.9); }
1128
+ }
1129
+
1130
+ @keyframes propertyNewsSlideIn {
1131
+ from { transform: translateY(-6px); opacity: 0; }
1132
+ to { transform: translateY(0); opacity: 1; }
1133
+ }
1134
+ `;
1135
+ document.head.appendChild(style);
1136
+ };
1137
+ var categoryStyles = {
1099
1138
  property: {
1100
1139
  label: "Property Update",
1101
- accent: "#f8d12f",
1102
- pillBg: "rgba(248, 209, 47, 0.12)",
1103
- pillBorder: "rgba(248, 209, 47, 0.45)",
1104
- pillColor: "#f8d12f",
1105
- icon: Building2
1140
+ backgroundColor: "rgba(14, 203, 129, 0.15)",
1141
+ borderColor: "rgba(14, 203, 129, 0.45)",
1142
+ color: "#0ecb81"
1106
1143
  },
1107
1144
  market: {
1108
- label: "Market Insight",
1109
- accent: "#0ecb81",
1110
- pillBg: "rgba(14, 203, 129, 0.12)",
1111
- pillBorder: "rgba(14, 203, 129, 0.45)",
1112
- pillColor: "#0ecb81",
1113
- icon: LineChart
1114
- },
1115
- portfolio: {
1116
- label: "Portfolio Signal",
1117
- accent: "#6c8cff",
1118
- pillBg: "rgba(108, 140, 255, 0.12)",
1119
- pillBorder: "rgba(108, 140, 255, 0.45)",
1120
- pillColor: "#c9d5ff",
1121
- icon: Sparkles
1145
+ label: "Market News",
1146
+ backgroundColor: "rgba(248, 209, 47, 0.15)",
1147
+ borderColor: "rgba(248, 209, 47, 0.45)",
1148
+ color: "#f8d12f"
1122
1149
  }
1123
1150
  };
1124
- var getTypeMeta = (type) => {
1125
- if (!type) return TYPE_META.property;
1126
- const key = type;
1127
- if (key in TYPE_META) return TYPE_META[key];
1128
- return {
1129
- label: type.toString(),
1130
- accent: "#f5f5f5",
1131
- pillBg: "rgba(255,255,255,0.08)",
1132
- pillBorder: "rgba(255,255,255,0.25)",
1133
- pillColor: "#ffffff",
1134
- icon: Newspaper
1135
- };
1136
- };
1137
1151
  var formatDate = (value) => {
1138
- const date = typeof value === "string" ? new Date(value) : value;
1139
- if (Number.isNaN(date.getTime())) return typeof value === "string" ? value : "";
1140
- return date.toLocaleDateString(void 0, {
1152
+ if (typeof value === "string") return value;
1153
+ if (!(value instanceof Date) || Number.isNaN(value.getTime())) return "";
1154
+ return value.toLocaleDateString(void 0, {
1141
1155
  month: "short",
1142
1156
  day: "numeric",
1143
1157
  year: "numeric"
1144
1158
  });
1145
1159
  };
1146
1160
  var PropertyNewsUpdates = React5.forwardRef(
1147
- ({ className, heading = "Latest property intelligence", subheading, items, emptyState, ...props }, ref) => {
1161
+ ({
1162
+ className,
1163
+ heading = "Property News & Headlines",
1164
+ subheading,
1165
+ items,
1166
+ emptyState,
1167
+ highlightFirst = true,
1168
+ ...props
1169
+ }, ref) => {
1170
+ const [page, setPage] = React5.useState(0);
1171
+ React5.useEffect(() => {
1172
+ ensureAnimationsInjected();
1173
+ }, []);
1148
1174
  const hasItems = Array.isArray(items) && items.length > 0;
1175
+ const totalPages = React5.useMemo(() => hasItems ? Math.max(1, Math.ceil(items.length / ITEMS_PER_PAGE)) : 1, [hasItems, items.length]);
1176
+ React5.useEffect(() => {
1177
+ setPage((prev) => Math.min(prev, totalPages - 1));
1178
+ }, [totalPages]);
1179
+ const paginatedItems = hasItems ? items.slice(page * ITEMS_PER_PAGE, page * ITEMS_PER_PAGE + ITEMS_PER_PAGE) : [];
1149
1180
  return /* @__PURE__ */ jsxs(
1150
1181
  "div",
1151
1182
  {
1152
1183
  ref,
1153
1184
  className: cn(
1154
- "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",
1185
+ "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)]",
1155
1186
  className
1156
1187
  ),
1157
1188
  ...props,
1158
1189
  children: [
1159
- /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
1160
- /* @__PURE__ */ jsx("p", { className: "text-[0.85rem] uppercase tracking-[0.25em] text-white/50", children: heading }),
1161
- subheading ? /* @__PURE__ */ jsx("p", { className: "text-sm text-white/60", children: subheading }) : null
1162
- ] }),
1163
- /* @__PURE__ */ jsx("div", { className: "mt-5 flex flex-col gap-4", children: hasItems ? items.map((item, index) => {
1164
- const meta = getTypeMeta(item.type);
1165
- const Icon = meta.icon;
1166
- const key = item.id ?? `${item.title}-${index}`;
1167
- const content = /* @__PURE__ */ jsxs(
1190
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1191
+ /* @__PURE__ */ jsxs("div", { children: [
1192
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold text-white", children: heading }),
1193
+ subheading ? /* @__PURE__ */ jsx("p", { className: "text-sm text-white/60", children: subheading }) : null
1194
+ ] }),
1195
+ /* @__PURE__ */ jsxs(
1168
1196
  "div",
1169
1197
  {
1170
- 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]",
1198
+ className: "inline-flex items-center font-semibold uppercase text-emerald-300",
1199
+ style: { gap: "0.35rem", fontSize: "0.8rem", letterSpacing: "0.15em" },
1171
1200
  children: [
1172
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
1173
- /* @__PURE__ */ jsx(
1174
- "div",
1175
- {
1176
- className: "flex h-12 w-12 items-center justify-center rounded-full border text-white",
1177
- style: {
1178
- background: meta.pillBg,
1179
- borderColor: meta.pillBorder,
1180
- color: meta.pillColor
1181
- },
1182
- children: /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5" })
1183
- }
1184
- ),
1185
- index !== items.length - 1 ? /* @__PURE__ */ jsx("div", { className: "mt-2 h-full w-px flex-1 bg-white/10" }) : null
1186
- ] }),
1187
- /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col", children: [
1188
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
1189
- /* @__PURE__ */ jsx("span", { className: "text-[0.7rem] font-semibold uppercase tracking-[0.3em]", style: { color: meta.accent }, children: meta.label }),
1190
- /* @__PURE__ */ jsxs("span", { className: "flex items-center text-xs text-white/60", children: [
1191
- /* @__PURE__ */ jsx(CalendarDays, { className: "mr-1 h-3.5 w-3.5" }),
1192
- formatDate(item.date)
1193
- ] })
1194
- ] }),
1195
- /* @__PURE__ */ jsx("p", { className: "mt-2 text-base font-semibold leading-snug", children: item.title }),
1196
- item.description ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-white/65", children: item.description }) : null,
1197
- (item.href || item.onAction) && item.actionLabel ? /* @__PURE__ */ jsxs(
1198
- "button",
1199
- {
1200
- type: "button",
1201
- className: "group mt-3 inline-flex items-center gap-1 text-sm font-semibold text-white transition hover:text-white/80",
1202
- onClick: () => {
1203
- if (item.href) {
1204
- window?.open?.(item.href, "_blank", "noopener,noreferrer");
1205
- }
1206
- item.onAction?.(item);
1207
- },
1208
- children: [
1209
- item.actionLabel,
1210
- /* @__PURE__ */ jsx(ArrowUpRight, { className: "h-4 w-4 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5" })
1211
- ]
1201
+ /* @__PURE__ */ jsx(
1202
+ "span",
1203
+ {
1204
+ style: {
1205
+ display: "inline-block",
1206
+ width: "6px",
1207
+ height: "6px",
1208
+ borderRadius: "50%",
1209
+ backgroundColor: "#34d399",
1210
+ animation: "propertyNewsPulse 2s infinite"
1212
1211
  }
1213
- ) : null
1214
- ] })
1212
+ }
1213
+ ),
1214
+ "LIVE"
1215
1215
  ]
1216
- },
1217
- key
1218
- );
1219
- return content;
1220
- }) : emptyState ?? /* @__PURE__ */ 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: [
1221
- /* @__PURE__ */ jsx(Newspaper, { className: "mb-3 h-8 w-8 text-white/40" }),
1222
- "No property news yet. Updates will land here as soon as we receive new intelligence."
1223
- ] }) })
1216
+ }
1217
+ )
1218
+ ] }),
1219
+ /* @__PURE__ */ jsx(
1220
+ "div",
1221
+ {
1222
+ className: "mt-4 flex flex-col gap-3",
1223
+ style: { minHeight: `${ITEMS_PER_PAGE * ROW_HEIGHT_PX}px` },
1224
+ children: hasItems ? paginatedItems.map((item, index) => {
1225
+ const absoluteIndex = page * ITEMS_PER_PAGE + index;
1226
+ const key = item.displayId ?? item.id ?? `${item.title}-${absoluteIndex}`;
1227
+ const styles = categoryStyles[item.type] ?? categoryStyles.market;
1228
+ const dateLabel = item.isNew ?? (highlightFirst && absoluteIndex === 0) ? "Just now" : typeof item.date === "string" && item.date.trim().length > 0 ? item.date : formatDate(item.date);
1229
+ const isHighlighted = item.isNew ?? (highlightFirst && absoluteIndex === 0);
1230
+ return /* @__PURE__ */ jsxs(
1231
+ "div",
1232
+ {
1233
+ style: {
1234
+ padding: "0.75rem",
1235
+ borderRadius: "6px",
1236
+ backgroundColor: isHighlighted ? "rgba(14, 203, 129, 0.1)" : "transparent",
1237
+ border: "1px solid transparent",
1238
+ transition: "background-color 0.2s",
1239
+ animation: item.isNew ? "propertyNewsSlideIn 0.5s ease-out" : void 0
1240
+ },
1241
+ children: [
1242
+ /* @__PURE__ */ jsx("p", { style: { fontSize: "0.9375rem", fontWeight: 500, marginBottom: "0.35rem" }, children: item.title }),
1243
+ /* @__PURE__ */ jsxs(
1244
+ "div",
1245
+ {
1246
+ style: {
1247
+ display: "flex",
1248
+ justifyContent: "space-between",
1249
+ alignItems: "center",
1250
+ fontSize: "0.75rem",
1251
+ color: "#b5b8c5"
1252
+ },
1253
+ children: [
1254
+ /* @__PURE__ */ jsx("span", { style: { color: isHighlighted ? "#0ecb81" : "inherit" }, children: dateLabel }),
1255
+ /* @__PURE__ */ jsx(
1256
+ "span",
1257
+ {
1258
+ style: {
1259
+ padding: "0.25rem 0.6rem",
1260
+ borderRadius: "4px",
1261
+ border: `1px solid ${styles.borderColor}`,
1262
+ backgroundColor: styles.backgroundColor,
1263
+ color: styles.color,
1264
+ fontSize: "0.68rem",
1265
+ fontWeight: 600,
1266
+ textTransform: "uppercase"
1267
+ },
1268
+ children: item.type === "property" ? styles.label : "Market News"
1269
+ }
1270
+ )
1271
+ ]
1272
+ }
1273
+ )
1274
+ ]
1275
+ },
1276
+ key
1277
+ );
1278
+ }) : emptyState ?? /* @__PURE__ */ 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: [
1279
+ /* @__PURE__ */ jsx(Newspaper, { className: "mb-3 h-8 w-8 text-white/40" }),
1280
+ "No property news yet. Updates will land here as soon as we receive new intelligence."
1281
+ ] })
1282
+ }
1283
+ ),
1284
+ hasItems && totalPages > 1 ? /* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center justify-between text-xs text-white/60", children: [
1285
+ /* @__PURE__ */ jsx(
1286
+ "button",
1287
+ {
1288
+ type: "button",
1289
+ onClick: () => setPage((prev) => Math.max(0, prev - 1)),
1290
+ disabled: page === 0,
1291
+ className: cn(
1292
+ "rounded-full border border-white/15 px-3 py-1 uppercase tracking-[0.2em]",
1293
+ page === 0 ? "opacity-40 cursor-not-allowed" : "hover:border-white/40"
1294
+ ),
1295
+ children: "Prev"
1296
+ }
1297
+ ),
1298
+ /* @__PURE__ */ jsxs("span", { className: "font-medium text-white/70", children: [
1299
+ "Page ",
1300
+ page + 1,
1301
+ " / ",
1302
+ totalPages
1303
+ ] }),
1304
+ /* @__PURE__ */ jsx(
1305
+ "button",
1306
+ {
1307
+ type: "button",
1308
+ onClick: () => setPage((prev) => Math.min(totalPages - 1, prev + 1)),
1309
+ disabled: page >= totalPages - 1,
1310
+ className: cn(
1311
+ "rounded-full border border-white/15 px-3 py-1 uppercase tracking-[0.2em]",
1312
+ page >= totalPages - 1 ? "opacity-40 cursor-not-allowed" : "hover:border-white/40"
1313
+ ),
1314
+ children: "Next"
1315
+ }
1316
+ )
1317
+ ] }) : null
1224
1318
  ]
1225
1319
  }
1226
1320
  );
@@ -1295,12 +1389,13 @@ var PropertyCompareBar = React5.forwardRef(
1295
1389
  {
1296
1390
  ref,
1297
1391
  className: cn(
1298
- "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",
1392
+ "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",
1299
1393
  className
1300
1394
  ),
1395
+ style: { borderRadius: "16px" },
1301
1396
  ...props,
1302
1397
  children: [
1303
- /* @__PURE__ */ jsxs("div", { className: "relative flex-1", ref: dropdownRef, children: [
1398
+ /* @__PURE__ */ jsxs("div", { className: "relative w-auto", ref: dropdownRef, children: [
1304
1399
  /* @__PURE__ */ jsxs(
1305
1400
  "button",
1306
1401
  {
@@ -1308,7 +1403,7 @@ var PropertyCompareBar = React5.forwardRef(
1308
1403
  disabled: !hasAddresses,
1309
1404
  onClick: () => setIsDropdownOpen((prev) => !prev),
1310
1405
  className: cn(
1311
- "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)]",
1406
+ "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",
1312
1407
  !hasAddresses && "text-white/40"
1313
1408
  ),
1314
1409
  children: [
@@ -1327,7 +1422,7 @@ var PropertyCompareBar = React5.forwardRef(
1327
1422
  ]
1328
1423
  }
1329
1424
  ),
1330
- isDropdownOpen && hasAddresses ? /* @__PURE__ */ 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) => {
1425
+ isDropdownOpen && hasAddresses ? /* @__PURE__ */ 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) => {
1331
1426
  const active = option.id === resolvedSelectedId;
1332
1427
  return /* @__PURE__ */ jsx(
1333
1428
  "button",
@@ -1349,7 +1444,7 @@ var PropertyCompareBar = React5.forwardRef(
1349
1444
  {
1350
1445
  variant: "accentOutline",
1351
1446
  size: "sm",
1352
- 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",
1447
+ 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",
1353
1448
  onClick: onCompareClick,
1354
1449
  disabled: !hasAddresses,
1355
1450
  children: [
@@ -1364,152 +1459,263 @@ var PropertyCompareBar = React5.forwardRef(
1364
1459
  }
1365
1460
  );
1366
1461
  PropertyCompareBar.displayName = "PropertyCompareBar";
1367
- var clampPct = (pct) => Math.min(100, Math.max(0, pct));
1368
- var EditIcon = ({ className }) => /* @__PURE__ */ jsxs(
1369
- "svg",
1370
- {
1371
- viewBox: "0 0 24 24",
1372
- fill: "none",
1373
- xmlns: "http://www.w3.org/2000/svg",
1374
- className,
1375
- children: [
1376
- /* @__PURE__ */ jsx(
1377
- "path",
1378
- {
1379
- d: "M12 20H21",
1380
- stroke: "currentColor",
1381
- strokeWidth: "2",
1382
- strokeLinecap: "round",
1383
- strokeLinejoin: "round"
1384
- }
1385
- ),
1386
- /* @__PURE__ */ jsx(
1387
- "path",
1388
- {
1389
- d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5Z",
1390
- stroke: "currentColor",
1391
- strokeWidth: "2",
1392
- strokeLinecap: "round",
1393
- strokeLinejoin: "round"
1394
- }
1395
- )
1396
- ]
1397
- }
1398
- );
1462
+ var TABS = [
1463
+ { id: "portfolio", label: "Portfolio" },
1464
+ { id: "openOrders", label: "Open Orders" },
1465
+ { id: "tradeHistory", label: "Trade History" },
1466
+ { id: "orderHistory", label: "Order History" }
1467
+ ];
1468
+ var panelStyle = {
1469
+ width: "100%",
1470
+ borderRadius: "16px",
1471
+ backgroundColor: "#111111",
1472
+ border: "1px solid rgba(255,255,255,0.08)",
1473
+ color: "#ffffff",
1474
+ display: "flex",
1475
+ flexDirection: "column"
1476
+ };
1477
+ var headerStyle = {
1478
+ padding: "1.25rem 1.5rem 0.5rem"
1479
+ };
1480
+ var titleStyle = {
1481
+ margin: 0,
1482
+ fontSize: "1.1rem",
1483
+ fontWeight: 600
1484
+ };
1485
+ var tabsRowStyle = {
1486
+ display: "flex",
1487
+ gap: "0.5rem",
1488
+ padding: "0 1.5rem",
1489
+ borderBottom: "1px solid rgba(255,255,255,0.08)"
1490
+ };
1491
+ var tabButtonBase = {
1492
+ background: "none",
1493
+ border: "none",
1494
+ color: "rgba(255,255,255,0.5)",
1495
+ fontSize: "0.9rem",
1496
+ fontWeight: 400,
1497
+ padding: "0.75rem 1rem",
1498
+ cursor: "pointer",
1499
+ position: "relative",
1500
+ transition: "color 0.2s ease"
1501
+ };
1502
+ var tabContentWrapper = {
1503
+ padding: "1.25rem 1.5rem 1.5rem",
1504
+ display: "flex",
1505
+ flexDirection: "column",
1506
+ gap: "0.75rem",
1507
+ flex: 1
1508
+ };
1509
+ var tableHeaderStyle = {
1510
+ display: "grid",
1511
+ gridTemplateColumns: "1.8fr 0.9fr 0.7fr 0.8fr 0.8fr 1fr",
1512
+ gap: "0.5rem",
1513
+ paddingBottom: "0.75rem",
1514
+ borderBottom: "1px solid rgba(255,255,255,0.1)",
1515
+ minWidth: "700px"
1516
+ };
1517
+ var tableHeaderCell = {
1518
+ fontSize: "0.7rem",
1519
+ fontWeight: 700,
1520
+ color: "#ffffff",
1521
+ textTransform: "uppercase",
1522
+ letterSpacing: "0.3px"
1523
+ };
1524
+ var emptyStateStyle = {
1525
+ flex: 1,
1526
+ display: "flex",
1527
+ alignItems: "center",
1528
+ justifyContent: "center",
1529
+ textAlign: "center",
1530
+ color: "rgba(255,255,255,0.6)",
1531
+ fontSize: "0.95rem",
1532
+ border: "1px dashed rgba(255,255,255,0.12)",
1533
+ borderRadius: "8px",
1534
+ minHeight: "220px"
1535
+ };
1536
+ var rowStyle = {
1537
+ display: "grid",
1538
+ gridTemplateColumns: "1.8fr 0.9fr 0.7fr 0.8fr 0.8fr 1fr",
1539
+ gap: "0.5rem",
1540
+ alignItems: "center",
1541
+ minWidth: "700px",
1542
+ padding: "0.75rem 0",
1543
+ borderBottom: "1px solid rgba(255,255,255,0.08)"
1544
+ };
1545
+ var formatCurrency = (value) => {
1546
+ if (value == null || Number.isNaN(value)) return "\u2014";
1547
+ return `$${value.toLocaleString(void 0, {
1548
+ minimumFractionDigits: 2,
1549
+ maximumFractionDigits: 2
1550
+ })}`;
1551
+ };
1552
+ var formatPercent = (value, fractionDigits = 2) => {
1553
+ if (value == null || Number.isNaN(value)) return "\u2014";
1554
+ return `${value.toFixed(fractionDigits)}%`;
1555
+ };
1399
1556
  var YourOrders = React5.forwardRef(
1400
- ({
1401
- className,
1402
- title = "Your Orders",
1403
- orders,
1404
- onEditPrice,
1405
- onEditAmount,
1406
- onCancel,
1407
- ...props
1408
- }, ref) => {
1409
- return /* @__PURE__ */ jsxs(
1410
- Card,
1411
- {
1412
- ref,
1413
- className: cn(
1414
- "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]",
1415
- className
1416
- ),
1417
- ...props,
1418
- children: [
1419
- /* @__PURE__ */ jsx(CardHeader, { className: "px-6 pb-2 pt-6", children: /* @__PURE__ */ jsx(CardTitle, { className: "m-0 text-[1.1rem] font-semibold text-white", children: title }) }),
1420
- /* @__PURE__ */ jsxs("div", { className: "px-6 pb-6 pt-2", children: [
1421
- /* @__PURE__ */ 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: [
1422
- /* @__PURE__ */ jsx("div", { className: "px-2", children: "Asset" }),
1423
- /* @__PURE__ */ jsx("div", { className: "px-2", children: "Type" }),
1424
- /* @__PURE__ */ jsx("div", { className: "px-2", children: "Date" }),
1425
- /* @__PURE__ */ jsx("div", { className: "px-2", children: "Price" }),
1426
- /* @__PURE__ */ jsx("div", { className: "px-2", children: "Amount" }),
1427
- /* @__PURE__ */ jsx("div", { className: "px-2", children: "Total" }),
1428
- /* @__PURE__ */ jsx("div", { className: "px-2", children: "Filled" }),
1429
- /* @__PURE__ */ jsx("div", { className: "px-2 text-center", children: "Actions" })
1430
- ] }),
1431
- orders.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-10 text-center text-sm text-white/50", children: "No orders" }) : /* @__PURE__ */ jsx("div", { children: orders.map((order) => {
1432
- const sideClass = order.side === "buy" ? "text-[#0ecb81]" : "text-[#f6465d]";
1433
- const filled = clampPct(order.filledPercent ?? 0);
1434
- const isCancelled = order.status?.toUpperCase() === "CANCELLED";
1435
- const canEditPrice = Boolean(onEditPrice);
1436
- const canEditAmount = Boolean(onEditAmount);
1437
- const canCancel = Boolean(onCancel);
1438
- return /* @__PURE__ */ jsxs(
1439
- "div",
1440
- {
1441
- 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]",
1442
- children: [
1443
- /* @__PURE__ */ jsx("div", { className: "truncate px-2 font-medium", children: order.asset }),
1444
- /* @__PURE__ */ jsx("div", { className: cn("px-2 font-medium uppercase", sideClass), children: order.side }),
1445
- /* @__PURE__ */ jsx("div", { className: "px-2 font-medium", children: order.date }),
1446
- /* @__PURE__ */ jsxs("div", { className: "px-2 font-medium", children: [
1447
- /* @__PURE__ */ jsxs("span", { className: "tabular-nums", children: [
1448
- "$",
1449
- order.price.toLocaleString()
1450
- ] }),
1451
- /* @__PURE__ */ jsx(
1452
- "button",
1453
- {
1454
- type: "button",
1455
- onClick: canEditPrice ? () => onEditPrice?.(order) : void 0,
1456
- className: cn(
1457
- "ml-1 inline-flex items-center justify-center text-white/60 transition-colors",
1458
- canEditPrice ? "cursor-pointer hover:text-[#C9A227] active:translate-y-px" : "cursor-default"
1459
- ),
1460
- "aria-label": "Edit price",
1461
- children: /* @__PURE__ */ jsx(EditIcon, { className: "h-[14px] w-[14px]" })
1462
- }
1463
- )
1464
- ] }),
1465
- /* @__PURE__ */ jsxs("div", { className: "px-2 font-medium", children: [
1466
- /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: order.amount.toLocaleString() }),
1467
- /* @__PURE__ */ jsx(
1468
- "button",
1557
+ ({ className, title = "Portfolio Holdings (Demo)", orders, ...props }, ref) => {
1558
+ const [activeTab, setActiveTab] = React5.useState("portfolio");
1559
+ const hasOrders = Array.isArray(orders) && orders.length > 0;
1560
+ const renderPortfolio = () => {
1561
+ if (!hasOrders) {
1562
+ return /* @__PURE__ */ jsx("div", { style: emptyStateStyle, children: "No holdings yet. Start trading to build your portfolio." });
1563
+ }
1564
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1565
+ /* @__PURE__ */ jsxs("div", { style: tableHeaderStyle, children: [
1566
+ /* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "Property" }),
1567
+ /* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "Value" }),
1568
+ /* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "Holding" }),
1569
+ /* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "% of Property" }),
1570
+ /* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "Avg Price" }),
1571
+ /* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "P&L" })
1572
+ ] }),
1573
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "0.65rem" }, children: orders.map((order) => {
1574
+ const propertyName = order.asset;
1575
+ const holding = order.holdingLabel ?? `${order.amount.toLocaleString(void 0, {
1576
+ minimumFractionDigits: 0,
1577
+ maximumFractionDigits: 2
1578
+ })} ${propertyName}`;
1579
+ const value = order.value ?? order.total;
1580
+ const portfolioShare = order.portfolioSharePercent != null ? `${order.portfolioSharePercent.toFixed(1)}% of portfolio` : void 0;
1581
+ const propertyPercent = order.propertyPercent;
1582
+ const avgPrice = order.avgEntryPrice ?? order.price;
1583
+ const pnlValue = order.pnlValue;
1584
+ const pnlPercent = order.pnlPercent;
1585
+ const pnlPositive = pnlValue != null ? pnlValue >= 0 : (pnlPercent ?? 0) >= 0;
1586
+ const currentPrice = order.currentPrice ?? order.price;
1587
+ const priceChangePercent = order.priceChangePercent ?? order.pnlPercent;
1588
+ const priceChangePositive = order.priceChangePositive ?? (priceChangePercent != null ? priceChangePercent >= 0 : void 0);
1589
+ return /* @__PURE__ */ jsxs("div", { style: rowStyle, children: [
1590
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "1rem", alignItems: "center" }, children: [
1591
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.15rem" }, children: [
1592
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "0.9rem", fontWeight: 500 }, children: propertyName }),
1593
+ portfolioShare ? /* @__PURE__ */ jsx("div", { style: { fontSize: "0.72rem", color: "rgba(255,255,255,0.5)" }, children: portfolioShare }) : null
1594
+ ] }),
1595
+ currentPrice != null ? /* @__PURE__ */ jsxs("div", { children: [
1596
+ /* @__PURE__ */ jsxs(
1597
+ "div",
1598
+ {
1599
+ style: {
1600
+ fontSize: "0.9rem",
1601
+ color: "var(--color-text, #ffffff)",
1602
+ display: "flex",
1603
+ alignItems: "center",
1604
+ gap: "0.25rem"
1605
+ },
1606
+ children: [
1607
+ priceChangePositive != null ? /* @__PURE__ */ jsx(
1608
+ "span",
1469
1609
  {
1470
- type: "button",
1471
- onClick: canEditAmount ? () => onEditAmount?.(order) : void 0,
1472
- className: cn(
1473
- "ml-1 inline-flex items-center justify-center text-white/60 transition-colors",
1474
- canEditAmount ? "cursor-pointer hover:text-[#C9A227] active:translate-y-px" : "cursor-default"
1475
- ),
1476
- "aria-label": "Edit amount",
1477
- children: /* @__PURE__ */ jsx(EditIcon, { className: "h-[14px] w-[14px]" })
1610
+ style: {
1611
+ color: priceChangePositive ? "#0ecb81" : "#f6465d",
1612
+ fontSize: "0.7em"
1613
+ },
1614
+ children: priceChangePositive ? "\u25B2" : "\u25BC"
1478
1615
  }
1479
- )
1480
- ] }),
1481
- /* @__PURE__ */ jsxs("div", { className: "px-2 font-medium tabular-nums", children: [
1482
- "$",
1483
- order.total.toLocaleString()
1484
- ] }),
1485
- /* @__PURE__ */ jsxs("div", { className: "px-2", children: [
1486
- /* @__PURE__ */ jsxs("div", { className: "text-[0.875rem] font-medium tabular-nums", children: [
1487
- filled.toFixed(0),
1488
- "%"
1489
- ] }),
1490
- /* @__PURE__ */ jsx("div", { className: "mt-1 h-1 w-4/5 overflow-hidden rounded-sm bg-white/10", children: /* @__PURE__ */ jsx("div", { className: "h-full rounded-sm bg-[#C9A227]", style: { width: `${filled}%` } }) })
1491
- ] }),
1492
- /* @__PURE__ */ jsx("div", { className: "px-2 text-center", children: isCancelled ? /* @__PURE__ */ 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__ */ jsx(
1493
- "button",
1494
- {
1495
- type: "button",
1496
- onClick: canCancel ? () => onCancel?.(order) : void 0,
1497
- className: cn(
1498
- "rounded border border-white/40 px-3 py-1 text-[0.8rem] text-white/70 transition-colors",
1499
- canCancel ? "cursor-pointer hover:bg-white/10 active:text-[#f6465d] active:border-[#f6465d] active:translate-y-px" : "cursor-default"
1500
- ),
1501
- children: "Cancel"
1502
- }
1503
- ) })
1504
- ]
1616
+ ) : null,
1617
+ formatCurrency(currentPrice)
1618
+ ]
1619
+ }
1620
+ ),
1621
+ priceChangePercent != null ? /* @__PURE__ */ jsxs(
1622
+ "div",
1623
+ {
1624
+ style: {
1625
+ fontSize: "0.7rem",
1626
+ color: priceChangePositive ? "#0ecb81" : "#f6465d"
1627
+ },
1628
+ children: [
1629
+ "(",
1630
+ priceChangePositive ? "+" : "",
1631
+ priceChangePercent.toFixed(2),
1632
+ "%)"
1633
+ ]
1634
+ }
1635
+ ) : null
1636
+ ] }) : null
1637
+ ] }),
1638
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "0.9rem", fontWeight: 500, color: "#D4AF37" }, children: formatCurrency(value) }),
1639
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "0.9rem", color: "rgba(255,255,255,0.92)" }, children: holding }),
1640
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "0.9rem", color: "rgba(255,255,255,0.8)" }, children: formatPercent(propertyPercent, 3) }),
1641
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "0.9rem", color: "rgba(255,255,255,0.9)" }, children: formatCurrency(avgPrice) }),
1642
+ /* @__PURE__ */ jsxs(
1643
+ "div",
1644
+ {
1645
+ style: {
1646
+ fontSize: "0.9rem",
1647
+ fontWeight: 500,
1648
+ color: pnlPositive ? "#0ecb81" : "#f6465d"
1505
1649
  },
1506
- order.id
1507
- );
1508
- }) })
1509
- ] })
1510
- ]
1511
- }
1512
- );
1650
+ children: [
1651
+ pnlValue != null ? `${pnlPositive ? "+" : "-"}${formatCurrency(Math.abs(pnlValue))}` : formatCurrency(pnlValue),
1652
+ pnlPercent != null ? /* @__PURE__ */ jsxs("span", { style: { marginLeft: "0.35rem", fontSize: "0.75rem", color: "rgba(255,255,255,0.6)" }, children: [
1653
+ "(",
1654
+ pnlPositive ? "+" : "",
1655
+ pnlPercent.toFixed(1),
1656
+ "%)"
1657
+ ] }) : null
1658
+ ]
1659
+ }
1660
+ )
1661
+ ] }, order.id);
1662
+ }) })
1663
+ ] });
1664
+ };
1665
+ const renderOtherTab = (label) => /* @__PURE__ */ jsx("div", { style: emptyStateStyle, children: `No ${label.toLowerCase()} data available.` });
1666
+ let tabContent = null;
1667
+ switch (activeTab) {
1668
+ case "portfolio":
1669
+ tabContent = renderPortfolio();
1670
+ break;
1671
+ case "openOrders":
1672
+ tabContent = renderOtherTab("Open Orders");
1673
+ break;
1674
+ case "tradeHistory":
1675
+ tabContent = renderOtherTab("Trade History");
1676
+ break;
1677
+ case "orderHistory":
1678
+ tabContent = renderOtherTab("Order History");
1679
+ break;
1680
+ default:
1681
+ tabContent = null;
1682
+ }
1683
+ return /* @__PURE__ */ jsxs("div", { ref, style: panelStyle, className, ...props, children: [
1684
+ /* @__PURE__ */ jsx("div", { style: headerStyle, children: /* @__PURE__ */ jsx("h3", { style: titleStyle, children: title }) }),
1685
+ /* @__PURE__ */ jsx("div", { style: tabsRowStyle, children: TABS.map((tab) => {
1686
+ const isActive = activeTab === tab.id;
1687
+ return /* @__PURE__ */ jsxs(
1688
+ "button",
1689
+ {
1690
+ type: "button",
1691
+ onClick: () => setActiveTab(tab.id),
1692
+ style: {
1693
+ ...tabButtonBase,
1694
+ color: isActive ? "#ffffff" : "rgba(255,255,255,0.55)",
1695
+ fontWeight: isActive ? 600 : 400
1696
+ },
1697
+ children: [
1698
+ tab.label,
1699
+ isActive ? /* @__PURE__ */ jsx(
1700
+ "div",
1701
+ {
1702
+ style: {
1703
+ position: "absolute",
1704
+ bottom: "-1px",
1705
+ left: 0,
1706
+ right: 0,
1707
+ height: "2px",
1708
+ background: "#f0b90b"
1709
+ }
1710
+ }
1711
+ ) : null
1712
+ ]
1713
+ },
1714
+ tab.id
1715
+ );
1716
+ }) }),
1717
+ /* @__PURE__ */ jsx("div", { style: tabContentWrapper, children: tabContent })
1718
+ ] });
1513
1719
  }
1514
1720
  );
1515
1721
  YourOrders.displayName = "YourOrders";
@@ -1719,7 +1925,10 @@ var PropertyHeroHeader = React5.forwardRef(
1719
1925
  ...props
1720
1926
  }, ref) => {
1721
1927
  const isPositive = changePercent == null ? void 0 : changePercent >= 0;
1722
- const tradeHoverColor = "var(--color-accent-hover, #f8d12f)";
1928
+ const accentColor = "#e6c87e";
1929
+ const tradeHoverColor = "#f5dd9a";
1930
+ const [isTradeInteracting, setIsTradeInteracting] = React5.useState(false);
1931
+ const [isOfferInteracting, setIsOfferInteracting] = React5.useState(false);
1723
1932
  return /* @__PURE__ */ jsxs(
1724
1933
  "div",
1725
1934
  {
@@ -1805,20 +2014,30 @@ var PropertyHeroHeader = React5.forwardRef(
1805
2014
  propertyTypeLabel == null ? null : /* @__PURE__ */ jsx("div", { children: propertyTypeLabel })
1806
2015
  ] })
1807
2016
  ] }),
1808
- /* @__PURE__ */ 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: [
2017
+ /* @__PURE__ */ 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: [
1809
2018
  /* @__PURE__ */ jsx(
1810
2019
  "button",
1811
2020
  {
1812
2021
  type: "button",
1813
2022
  onClick: onTrade,
1814
- 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]",
1815
- style: { backgroundColor: "var(--color-accent, #e6c87e)", color: "black" },
1816
- onMouseEnter: (e) => {
1817
- e.currentTarget.style.backgroundColor = tradeHoverColor;
1818
- },
1819
- onMouseLeave: (e) => {
1820
- e.currentTarget.style.backgroundColor = "var(--color-accent, #e6c87e)";
2023
+ 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]",
2024
+ style: {
2025
+ backgroundColor: isTradeInteracting ? tradeHoverColor : accentColor,
2026
+ color: "black",
2027
+ width: "88.06px",
2028
+ height: "43px",
2029
+ minWidth: "88.06px",
2030
+ borderColor: isTradeInteracting ? accentColor : "transparent",
2031
+ boxShadow: isTradeInteracting ? `0 0 0 2px rgba(0,0,0,0.4), 0 0 0 4px ${accentColor}` : "none"
1821
2032
  },
2033
+ onMouseEnter: () => setIsTradeInteracting(true),
2034
+ onMouseLeave: () => setIsTradeInteracting(false),
2035
+ onMouseDown: () => setIsTradeInteracting(true),
2036
+ onMouseUp: () => setIsTradeInteracting(false),
2037
+ onFocus: () => setIsTradeInteracting(true),
2038
+ onBlur: () => setIsTradeInteracting(false),
2039
+ onTouchStart: () => setIsTradeInteracting(true),
2040
+ onTouchEnd: () => setIsTradeInteracting(false),
1822
2041
  children: "Trade"
1823
2042
  }
1824
2043
  ),
@@ -1827,18 +2046,24 @@ var PropertyHeroHeader = React5.forwardRef(
1827
2046
  {
1828
2047
  type: "button",
1829
2048
  onClick: onMakeOffer,
1830
- 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]",
2049
+ 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]",
1831
2050
  style: {
1832
- backgroundColor: "transparent",
1833
- borderColor: "var(--color-accent, #e6c87e)",
1834
- color: "var(--color-accent, #e6c87e)"
1835
- },
1836
- onMouseEnter: (e) => {
1837
- e.currentTarget.style.backgroundColor = "rgba(240, 185, 11, 0.1)";
1838
- },
1839
- onMouseLeave: (e) => {
1840
- e.currentTarget.style.backgroundColor = "transparent";
2051
+ backgroundColor: isOfferInteracting ? accentColor : "transparent",
2052
+ borderColor: accentColor,
2053
+ color: isOfferInteracting ? "black" : accentColor,
2054
+ width: "127.14px",
2055
+ height: "43px",
2056
+ minWidth: "127.14px",
2057
+ boxShadow: isOfferInteracting ? `0 0 0 2px rgba(0,0,0,0.4), 0 0 0 4px ${accentColor}` : "none"
1841
2058
  },
2059
+ onMouseEnter: () => setIsOfferInteracting(true),
2060
+ onMouseLeave: () => setIsOfferInteracting(false),
2061
+ onMouseDown: () => setIsOfferInteracting(true),
2062
+ onMouseUp: () => setIsOfferInteracting(false),
2063
+ onFocus: () => setIsOfferInteracting(true),
2064
+ onBlur: () => setIsOfferInteracting(false),
2065
+ onTouchStart: () => setIsOfferInteracting(true),
2066
+ onTouchEnd: () => setIsOfferInteracting(false),
1842
2067
  children: "Make Offer"
1843
2068
  }
1844
2069
  )