brickwise 0.1.7 → 0.1.21

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.7",
3
+ "version": "0.1.21",
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",
@@ -43,6 +43,7 @@
43
43
  "dependencies": {
44
44
  "better-sqlite3": "^12.11.1",
45
45
  "leaflet": "^1.9.4",
46
+ "leaflet.heat": "^0.2.0",
46
47
  "next": "16.2.10",
47
48
  "react": "19.2.4",
48
49
  "react-dom": "19.2.4"
@@ -51,6 +52,7 @@
51
52
  "@tailwindcss/postcss": "^4",
52
53
  "@types/better-sqlite3": "^7.6.13",
53
54
  "@types/leaflet": "^1.9.21",
55
+ "@types/leaflet.heat": "^0.2.5",
54
56
  "@types/node": "^20",
55
57
  "@types/react": "^19",
56
58
  "@types/react-dom": "^19",
@@ -0,0 +1,16 @@
1
+ import { NextResponse } from "next/server";
2
+ import { poisForPoints } from "@/lib/pois";
3
+ import { listFavorites } from "@/lib/store";
4
+
5
+ /** All cafés near any located favorite, in a single Overpass query. */
6
+ export async function GET() {
7
+ try {
8
+ const points = listFavorites()
9
+ .filter((f) => f.lat != null && f.lon != null)
10
+ .map((f) => ({ lat: f.lat!, lon: f.lon! }));
11
+ const { pois, failed } = await poisForPoints(points, "cafe");
12
+ return NextResponse.json({ cafes: pois, favoritesQueried: points.length, failed });
13
+ } catch (e) {
14
+ return NextResponse.json({ error: (e as Error).message }, { status: 500 });
15
+ }
16
+ }
@@ -1,29 +1,15 @@
1
1
  import { NextResponse } from "next/server";
2
- import { gymsNear } from "@/lib/gyms";
2
+ import { poisForPoints } from "@/lib/pois";
3
3
  import { listFavorites } from "@/lib/store";
4
4
 
5
- /** All gyms within 2 km of every located favorite, deduplicated. */
5
+ /** All gyms near any located favorite, in a single Overpass query. */
6
6
  export async function GET() {
7
7
  try {
8
- const favorites = listFavorites().filter((f) => f.lat != null && f.lon != null);
9
- const seen = new Map<string, { name: string; lat: number; lon: number }>();
10
- let failed = 0;
11
- for (const f of favorites) {
12
- const gyms = await gymsNear(f.lat!, f.lon!);
13
- if (gyms == null) {
14
- failed++;
15
- continue;
16
- }
17
- for (const g of gyms) {
18
- const key = `${g.lat.toFixed(5)},${g.lon.toFixed(5)}`;
19
- if (!seen.has(key)) seen.set(key, { name: g.name, lat: g.lat, lon: g.lon });
20
- }
21
- }
22
- return NextResponse.json({
23
- gyms: [...seen.values()],
24
- favoritesQueried: favorites.length,
25
- failed,
26
- });
8
+ const points = listFavorites()
9
+ .filter((f) => f.lat != null && f.lon != null)
10
+ .map((f) => ({ lat: f.lat!, lon: f.lon! }));
11
+ const { pois, failed } = await poisForPoints(points, "gym");
12
+ return NextResponse.json({ gyms: pois, favoritesQueried: points.length, failed });
27
13
  } catch (e) {
28
14
  return NextResponse.json({ error: (e as Error).message }, { status: 500 });
29
15
  }
@@ -0,0 +1,35 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import { listFavorites, txCountByTowerSince } from "@/lib/store";
3
+
4
+ const MONTHS = 12;
5
+
6
+ /**
7
+ * Liquidity of each located favorite's tower = DLD transactions registered in the
8
+ * last 12 months, split by kind via ?kind=buy|rent (defaults to rent). Feeds the
9
+ * heatmap: each favorite is a weighted point, so nearby towers blend into a
10
+ * "how active is this pocket" surface for that market (rentals vs sales).
11
+ */
12
+ export async function GET(req: NextRequest) {
13
+ try {
14
+ const kindParam = req.nextUrl.searchParams.get("kind");
15
+ const kind: "buy" | "rent" = kindParam === "buy" ? "buy" : "rent";
16
+ const since = new Date();
17
+ since.setMonth(since.getMonth() - MONTHS);
18
+ const sinceISO = since.toISOString().slice(0, 10);
19
+ const counts = txCountByTowerSince(sinceISO, kind);
20
+
21
+ const points = listFavorites()
22
+ .filter((f) => f.lat != null && f.lon != null)
23
+ .map((f) => ({
24
+ id: f.id,
25
+ lat: f.lat as number,
26
+ lon: f.lon as number,
27
+ weight: f.tower_slug ? counts.get(f.tower_slug) ?? 0 : 0,
28
+ }));
29
+
30
+ const max = points.reduce((m, p) => Math.max(m, p.weight), 0);
31
+ return NextResponse.json({ points, max, months: MONTHS, kind });
32
+ } catch (e) {
33
+ return NextResponse.json({ error: (e as Error).message }, { status: 500 });
34
+ }
35
+ }