Files
dashboard-cpsp/components/feedInButton/FeedInButtonPanel.tsx
T

150 lines
5.1 KiB
TypeScript

import React, { useEffect, useMemo, useState } from 'react';
import { api, errorMessage } from '../../services/apiClient.ts';
import type { Kandang, Site } from '../../types/api.ts';
import { Banner } from '../ui/Feedback.tsx';
import FeedInButtonGrid from './FeedInButtonGrid.tsx';
type FeedInButtonPanelProps = {
variant?: 'default' | 'kiosk';
};
const hasConfiguredUrls = (kandang: Kandang): boolean =>
(kandang.feed_in_button_urls ?? []).some((entry) => entry.url.trim());
const FeedInButtonPanel: React.FC<FeedInButtonPanelProps> = ({ variant = 'default' }) => {
const [sites, setSites] = useState<Site[]>([]);
const [kandangs, setKandangs] = useState<Kandang[]>([]);
const [selectedSiteId, setSelectedSiteId] = useState<number | null>(null);
const [selectedKandangId, setSelectedKandangId] = useState<number | 'all'>('all');
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const isKiosk = variant === 'kiosk';
useEffect(() => {
let cancelled = false;
(async () => {
setLoading(true);
setError(null);
try {
const nextSites = await api.sites.list();
if (cancelled) return;
setSites(nextSites);
setSelectedSiteId((prev) => {
if (prev && nextSites.some((site) => site.id === prev)) return prev;
return nextSites[0]?.id ?? null;
});
} catch (err) {
if (!cancelled) setError(errorMessage(err));
} finally {
if (!cancelled) setLoading(false);
}
})();
return () => {
cancelled = true;
};
}, []);
useEffect(() => {
if (selectedSiteId == null) {
setKandangs([]);
return;
}
let cancelled = false;
(async () => {
setLoading(true);
setError(null);
try {
const nextKandangs = await api.kandangs.list({ site_id: selectedSiteId });
if (cancelled) return;
setKandangs(nextKandangs);
setSelectedKandangId('all');
} catch (err) {
if (!cancelled) setError(errorMessage(err));
} finally {
if (!cancelled) setLoading(false);
}
})();
return () => {
cancelled = true;
};
}, [selectedSiteId]);
const filterableKandangs = useMemo(() => kandangs.filter(hasConfiguredUrls), [kandangs]);
const displayedKandangs = useMemo(() => {
if (selectedKandangId === 'all') return filterableKandangs;
return filterableKandangs.filter((kandang) => kandang.id === selectedKandangId);
}, [filterableKandangs, selectedKandangId]);
const chipClass = (active: boolean) =>
`px-4 py-2 rounded-full text-sm font-semibold border transition-colors ${
active
? 'bg-red-600 text-white border-red-600 shadow-sm'
: 'bg-white text-gray-700 border-gray-300 hover:border-red-300 hover:text-red-600'
}`;
return (
<div className={isKiosk ? 'max-w-5xl mx-auto space-y-6' : 'space-y-6'}>
{error && <Banner kind="error">{error}</Banner>}
{(sites.length > 1 || filterableKandangs.length > 0) && (
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-4 space-y-4">
{sites.length > 1 && (
<div className="flex flex-col sm:flex-row sm:items-center gap-2">
<label className="text-sm font-medium text-gray-700 shrink-0" htmlFor="feed-in-site">
Site
</label>
<select
id="feed-in-site"
value={selectedSiteId ?? ''}
onChange={(e) => setSelectedSiteId(e.target.value ? Number(e.target.value) : null)}
className="border border-gray-300 rounded-md px-3 py-2 text-sm w-full sm:max-w-xs focus:ring-red-500 focus:border-red-500"
>
{sites.map((site) => (
<option key={site.id} value={site.id}>
{site.site_name}
</option>
))}
</select>
</div>
)}
{filterableKandangs.length > 0 && (
<div>
<p className="text-sm font-medium text-gray-700 mb-2">Kandang</p>
<div className="flex flex-wrap gap-2">
<button
type="button"
className={chipClass(selectedKandangId === 'all')}
onClick={() => setSelectedKandangId('all')}
>
Semua
</button>
{filterableKandangs.map((kandang) => (
<button
key={kandang.id}
type="button"
className={chipClass(selectedKandangId === kandang.id)}
onClick={() => setSelectedKandangId(kandang.id)}
>
{kandang.kandang_name}
</button>
))}
</div>
</div>
)}
</div>
)}
{loading ? (
<div className="text-center text-sm text-gray-500 py-10">Memuat kandang...</div>
) : (
<FeedInButtonGrid kandangs={displayedKandangs} variant={variant} />
)}
</div>
);
};
export default FeedInButtonPanel;