Python 68.8%
TypeScript 18.6%
CSS 8.7%
JavaScript 3.3%
HTML 0.6%
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