File size: 4,038 Bytes
41c19e3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import React, { useEffect, useState } from 'react';
import { Phone, MapPin, Loader2 } from 'lucide-react';
import { apiFetch } from '@/lib/api';

function formatWhen(iso) {
    if (!iso) return '';
    const d = new Date(iso);
    if (Number.isNaN(d.getTime())) return '';
    return d.toLocaleString(undefined, {
        month: 'short',
        day: 'numeric',
        hour: 'numeric',
        minute: '2-digit',
    });
}

/**
 * Chronological Call/Visit activity feed. Self-fetching — pass `endpoint` (the GET URL) and bump
 * `refreshKey` after logging a new activity elsewhere to re-fetch.
 */
export default function ActivityTimeline({ endpoint, refreshKey, heading = 'Activity timeline', className }) {
    const [activities, setActivities] = useState([]);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        if (!endpoint) return;
        let cancelled = false;
        setLoading(true);
        (async () => {
            try {
                const res = await apiFetch(endpoint);
                if (!res.ok) return;
                const data = await res.json();
                if (!cancelled) setActivities(data.activities || []);
            } catch (e) {
                console.error(e);
            } finally {
                if (!cancelled) setLoading(false);
            }
        })();
        return () => {
            cancelled = true;
        };
    }, [endpoint, refreshKey]);

    if (!endpoint) return null;

    return (
        <div className={className}>
            <h4 className="mb-3 font-semibold text-slate-800">{heading}</h4>
            {loading ? (
                <div className="flex items-center gap-2 text-sm text-slate-500">
                    <Loader2 className="h-4 w-4 animate-spin" aria-hidden />
                    Loading…
                </div>
            ) : activities.length === 0 ? (
                <p className="text-sm text-slate-500">No calls or visits logged yet.</p>
            ) : (
                <ul className="space-y-2">
                    {activities.map((a) => (
                        <li
                            key={a.id}
                            className="flex gap-2.5 rounded-lg border border-slate-200 bg-white px-3 py-2"
                        >
                            <div
                                className={
                                    'mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full ' +
                                    (a.activity_type === 'call' ? 'bg-emerald-100 text-emerald-700' : 'bg-violet-100 text-violet-700')
                                }
                            >
                                {a.activity_type === 'call' ? (
                                    <Phone className="h-3.5 w-3.5" aria-hidden />
                                ) : (
                                    <MapPin className="h-3.5 w-3.5" aria-hidden />
                                )}
                            </div>
                            <div className="min-w-0 flex-1">
                                <p className="text-xs text-slate-500">
                                    <span className="font-medium text-slate-700">
                                        {a.activity_type === 'call' ? 'Call' : 'Visit'}
                                    </span>
                                    {a.contact_name ? <span> with {a.contact_name}</span> : null}
                                    {a.user_display_name ? <span> · {a.user_display_name}</span> : null}
                                    {a.created_at ? <span> · {formatWhen(a.created_at)}</span> : null}
                                </p>
                                {a.notes ? (
                                    <p className="mt-0.5 whitespace-pre-wrap text-sm text-slate-800">{a.notes}</p>
                                ) : null}
                            </div>
                        </li>
                    ))}
                </ul>
            )}
        </div>
    );
}