// components/dashboards/SiteCard.tsx 'use client'; import React, { useEffect, useMemo, useState } from 'react'; import Link from 'next/link'; import { formatAddress } from '@/app/utils/formatAddress'; import { formatCrmTimestamp } from '@/app/utils/datetime'; type CrmProject = { name: string; // e.g. PROJ-0008 (siteId) project_name: string; status?: string; percent_complete?: number | null; owner?: string | null; modified?: string | null; customer?: string | null; project_type?: string | null; custom_address?: string | null; custom_email?: string | null; custom_mobile_phone_no?: string | null; }; interface SiteCardProps { siteId: string; // CRM Project "name" (canonical id) className?: string; // optional styling hook fallbackStatus?: string; // optional backup status if CRM is missing it } const API = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:8000'; const SiteCard: React.FC = ({ siteId, className = '', fallbackStatus }) => { const [project, setProject] = useState(null); const [loading, setLoading] = useState(true); const [err, setErr] = useState(null); useEffect(() => { let cancelled = false; const fetchProject = async () => { setLoading(true); setErr(null); try { // ---- Try a single-project endpoint first (best) ---- // e.g. GET /crm/projects/PROJ-0008 const single = await fetch(`${API}/crm/projects/${encodeURIComponent(siteId)}`); if (single.ok) { const pj = await single.json(); if (!cancelled) setProject(pj?.data ?? pj ?? null); } else { // ---- Fallback: fetch all and find by name (works with your existing API) ---- const list = await fetch(`${API}/crm/projects?limit=0`); if (!list.ok) throw new Error(await list.text()); const json = await list.json(); const found = (json?.data ?? []).find((p: CrmProject) => p.name === siteId) ?? null; if (!cancelled) setProject(found); } } catch (e: any) { if (!cancelled) setErr(e?.message ?? 'Failed to load CRM project'); } finally { if (!cancelled) setLoading(false); } }; fetchProject(); return () => { cancelled = true; }; }, [siteId]); const status = project?.status || fallbackStatus || 'Unknown'; const statusColorClass = status === 'Active' ? 'text-green-500' : status === 'Inactive' ? 'text-orange-500' : 'text-red-500'; const niceAddress = useMemo(() => { if (!project?.custom_address) return 'N/A'; return formatAddress(project.custom_address).multiLine; }, [project?.custom_address]); const lastSync = useMemo(() => { return formatCrmTimestamp(project?.modified, { includeSeconds: true }) || 'N/A'; }, [project?.modified]); const inverterProvider = project?.project_type || 'N/A'; const emergencyContact = project?.custom_mobile_phone_no || project?.custom_email || project?.customer || 'N/A'; return (

{project?.project_name || siteId}

{loading ? (
) : err ? (
Failed to load CRM: {err}
) : !project ? (
No CRM project found for {siteId}.
) : ( <>

Status:

{status}

Location:

{niceAddress}

Inverter Provider:

{inverterProvider}

Emergency Contact:

{emergencyContact}

Last Sync:

{lastSync}

)} View Dashboard
); }; export default SiteCard;