SPB Git forge

spb/rent-ka

Public
8commits 1branches 0releases
7.4 MBsize
maindefault branch
19 days agolast push
Python 68.8% TypeScript 18.6% CSS 8.7% JavaScript 3.3% HTML 0.6%
2.9 KB · 82 lines tsx
Raw Blame History
1// -----------------------------------------------------------------------------2// Rent-Ka — Rental listings aggregator (Canada, outside Québec)3// Author: Simon-Pierre Boucher — contact@spboucher.ai4// pages/Sources.tsx: registry of aggregated sources — property managers5// -----------------------------------------------------------------------------6import { useEffect, useState } from "react";7import { Source, fetchSources } from "../api";89export default function SourcesPage() {10  const [sources, setSources] = useState<Source[] | null>(null);11  const [error, setError] = useState<string | null>(null);1213  useEffect(() => {14    fetchSources()15      .then((r) => setSources(r.sources))16      .catch((e) => setError(String(e)));17  }, []);1819  return (20    <div className="container sources">21      <span className="kicker">Registry — aggregated sources</span>22      <h1>Aggregated sources</h1>23      <p className="sub">24        All the sources tracked by Rent-Ka: property managers and rental25        platforms across Canada (outside Québec). Every "connected" source is26        synced periodically by a dedicated connector.27      </p>2829      {error && <div className="notice">⚠️ {error}</div>}30      {!sources && !error && <div className="notice">Loading…</div>}3132      {sources && (33        <>34        <h2 style={{ marginTop: 28 }}>Monthly rentals — property managers</h2>35        <p className="sub">36          Property managers across Canada; sources without a connector are37          pending.38        </p>39        <div className="src-wrap">40        <table className="src-table">41          <thead>42            <tr>43              <th>Manager</th>44              <th>Areas</th>45              <th>Status</th>46              <th style={{ textAlign: "right" }}>Active listings</th>47              <th>Last sync</th>48            </tr>49          </thead>50          <tbody>51            {sources.map((s) => (52              <tr key={s.id}>53                <td>54                  <a href={s.url} target="_blank" rel="noopener noreferrer">{s.name}</a>55                </td>56                <td style={{ color: "var(--ink-2)", maxWidth: 380 }}>{s.sectors}</td>57                <td>58                  {s.connector ? (59                    <span className="pill ok">connected</span>60                  ) : (61                    <span className="pill todo">{s.status}</span>62                  )}63                </td>64                <td style={{ textAlign: "right" }}>65                  <span className="count-pill">{s.active_listings || "—"}</span>66                </td>67                <td style={{ color: "var(--ink-3)" }}>68                  {s.last_sync69                    ? new Date(s.last_sync * 1000).toLocaleString("en-CA")70                    : "—"}71                </td>72              </tr>73            ))}74          </tbody>75        </table>76        </div>77        </>78      )}79    </div>80  );81}82