brickwise 0.1.5 → 0.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brickwise",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "App local para analizar la rentabilidad de alquiler de inmuebles de Property Finder UAE con el histórico real de transacciones DLD.",
5
5
  "license": "MIT",
6
6
  "author": "eljommys",
@@ -38,15 +38,27 @@ function matchesBedFilter(r: { bedrooms: number | null }, bedFilter: number[]):
38
38
  return bedFilter.length === 0 || (r.bedrooms != null && bedFilter.includes(r.bedrooms));
39
39
  }
40
40
 
41
- function yieldColor(y: number | null): string {
42
- if (y == null) return "#737373";
43
- if (y >= 0.07) return "#059669";
44
- if (y >= 0.055) return "#65a30d";
45
- if (y >= 0.04) return "#d97706";
46
- return "#dc2626";
41
+ /**
42
+ * Relative color scale: red (lowest yield in the set) turquoise (highest).
43
+ * Absolute thresholds made every Dubai listing green because yields are all high;
44
+ * scaling to the actual min/max makes the differences readable on the map.
45
+ */
46
+ function yieldColor(y: number | null, min: number, max: number): string {
47
+ if (y == null) return "#8a8a8a";
48
+ const t = max > min ? Math.min(1, Math.max(0, (y - min) / (max - min))) : 0.5;
49
+ const hue = t * 174; // 0° red → 174° turquoise
50
+ return `hsl(${Math.round(hue)}, 66%, 44%)`;
47
51
  }
48
52
 
49
- function popupHtml(r: FavRow): string {
53
+ /** Min/max of the located favorites' yields, for the relative color scale. */
54
+ function yieldRange(rows: FavRow[] | null): { min: number; max: number } {
55
+ const ys = (rows ?? [])
56
+ .filter((r) => r.lat != null && r.lon != null && r.gross_yield != null)
57
+ .map((r) => r.gross_yield as number);
58
+ return ys.length ? { min: Math.min(...ys), max: Math.max(...ys) } : { min: 0, max: 1 };
59
+ }
60
+
61
+ function popupHtml(r: FavRow, min: number, max: number): string {
50
62
  const img = r.images?.[0]?.medium || r.images?.[0]?.small;
51
63
  const yieldTxt = r.gross_yield != null ? (r.gross_yield * 100).toFixed(1) + " %" : "sin datos";
52
64
  return `
@@ -55,7 +67,7 @@ function popupHtml(r: FavRow): string {
55
67
  <div style="font-weight:600;margin-top:6px;line-height:1.3">${r.title}</div>
56
68
  <div style="color:#666;font-size:12px;margin-top:2px">${r.tower_name ?? ""}</div>
57
69
  <div style="margin-top:4px;font-size:14px">
58
- Rentabilidad: <span style="color:${yieldColor(r.gross_yield)};font-weight:700">${yieldTxt}</span>
70
+ Rentabilidad: <span style="color:${yieldColor(r.gross_yield, min, max)};font-weight:700">${yieldTxt}</span>
59
71
  </div>
60
72
  <div style="margin-top:6px;display:flex;gap:10px;font-size:12px">
61
73
  <a href="/listing/${r.id}">Ver análisis</a>
@@ -193,19 +205,20 @@ export default function FavoritesMapPage() {
193
205
  if (!L || !map || !mapReady || !rows) return;
194
206
  Object.values(markersRef.current).forEach((m) => m.remove());
195
207
  markersRef.current = {};
208
+ const { min, max } = yieldRange(rows);
196
209
  const bounds: [number, number][] = [];
197
210
  for (const r of rows) {
198
211
  if (r.lat == null || r.lon == null) continue;
199
212
  const y = r.gross_yield != null ? (r.gross_yield * 100).toFixed(1) + "%" : "—";
200
213
  const icon = L.divIcon({
201
214
  className: "",
202
- html: `<div class="bw-pin" style="--pin-color:${yieldColor(r.gross_yield)}"><div class="bw-pin-label">${y}</div><div class="bw-pin-tail"></div></div>`,
215
+ html: `<div class="bw-pin" style="--pin-color:${yieldColor(r.gross_yield, min, max)}"><div class="bw-pin-label">${y}</div><div class="bw-pin-tail"></div></div>`,
203
216
  iconSize: [0, 0],
204
217
  iconAnchor: [0, 0],
205
218
  });
206
219
  const marker = L.marker([r.lat, r.lon], { icon })
207
220
  .addTo(map)
208
- .bindPopup(popupHtml(r), { maxWidth: 260, offset: [0, -34] });
221
+ .bindPopup(popupHtml(r, min, max), { maxWidth: 260, offset: [0, -34] });
209
222
  marker.on("mouseover", () => {
210
223
  setHoveredId(r.id);
211
224
  document.getElementById(`fav-${r.id}`)?.scrollIntoView({ block: "nearest", behavior: "smooth" });
@@ -667,7 +680,26 @@ export default function FavoritesMapPage() {
667
680
  />
668
681
 
669
682
  {/* ------------------------------------------------ map */}
670
- <div ref={containerRef} className="min-h-0 min-w-0 flex-1" />
683
+ <div className="relative min-h-0 min-w-0 flex-1">
684
+ <div ref={containerRef} className="absolute inset-0" />
685
+ {(() => {
686
+ const { min, max } = yieldRange(rows);
687
+ if (!(max > min)) return null;
688
+ return (
689
+ <div className="pointer-events-none absolute bottom-4 left-3 z-[500] rounded border border-black/10 bg-white/90 px-2 py-1.5 text-[10px] text-neutral-700 shadow-lg dark:border-white/10 dark:bg-neutral-900/90 dark:text-neutral-200">
690
+ <div className="btn-font mb-1 text-[9px] text-neutral-500">Rentabilidad</div>
691
+ <div className="flex items-center gap-1.5">
692
+ <span>{(min * 100).toFixed(1)}%</span>
693
+ <span
694
+ className="h-2 w-24 rounded-sm"
695
+ style={{ background: `linear-gradient(90deg, hsl(0,66%,44%), hsl(87,66%,44%), hsl(174,66%,44%))` }}
696
+ />
697
+ <span>{(max * 100).toFixed(1)}%</span>
698
+ </div>
699
+ </div>
700
+ );
701
+ })()}
702
+ </div>
671
703
  </div>
672
704
  );
673
705
  }
@@ -26,11 +26,11 @@
26
26
  }
27
27
 
28
28
  @theme {
29
- /* Neutral dark grays anchored to the #2c2c2c (44,44,44) background */
30
- --color-neutral-950: #242424; /* deep insets / header */
31
- --color-neutral-900: #2c2c2c; /* panels & cards == background */
32
- --color-neutral-800: #383838; /* borders, dividers, hover (subtle) */
33
- --color-neutral-700: #4a4a4a;
29
+ /* Neutral dark grays anchored to the #181818 background */
30
+ --color-neutral-950: #121212; /* deep insets / header */
31
+ --color-neutral-900: #181818; /* panels & cards == background */
32
+ --color-neutral-800: #2a2a2a; /* borders, dividers, hover (subtle) */
33
+ --color-neutral-700: #3a3a3a;
34
34
 
35
35
  /* Brick accent — remaps every blue-* usage in the app to color ladrillo */
36
36
  --color-blue-50: #faeee8;
@@ -58,7 +58,7 @@
58
58
 
59
59
  @media (prefers-color-scheme: dark) {
60
60
  :root {
61
- --background: #2c2c2c; /* gris muy oscuro (44,44,44) */
61
+ --background: #181818;
62
62
  --foreground: #ededed;
63
63
  }
64
64
  }