ai insight rework #2
This commit is contained in:
1 parent
9d76f54d33
commit
e1987d6999
39 files changed
+2311
-830
No files matched your search
+86
-85
@@ -55,7 +55,9 @@ const SettingsPage: React.FC = () => {
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [siteName, setSiteName] = useState('');
|
||||
const [kandangName, setKandangName] = useState('');
|
||||
const [feedInButtonUrls, setFeedInButtonUrls] = useState<FeedInButtonUrl[]>([emptyFeedInButtonUrl()]);
|
||||
const [feedInButtonUrls, setFeedInButtonUrls] = useState<FeedInButtonUrl[]>([
|
||||
emptyFeedInButtonUrl(),
|
||||
]);
|
||||
const [flockName, setFlockName] = useState('');
|
||||
const [editingCycleId, setEditingCycleId] = useState<number | null>(null);
|
||||
const [startDate, setStartDate] = useState(defaultCycleForm().startDate);
|
||||
@@ -135,7 +137,9 @@ const SettingsPage: React.FC = () => {
|
||||
|
||||
React.useEffect(() => {
|
||||
const urls = selectedKandang?.feed_in_button_urls ?? [];
|
||||
setFeedInButtonUrls(urls.length > 0 ? urls.map((entry) => ({ ...entry })) : [emptyFeedInButtonUrl()]);
|
||||
setFeedInButtonUrls(
|
||||
urls.length > 0 ? urls.map((entry) => ({ ...entry })) : [emptyFeedInButtonUrl()]
|
||||
);
|
||||
}, [selectedKandang?.id, selectedKandang?.feed_in_button_urls]);
|
||||
|
||||
const wrap = async (fn: () => Promise<void>) => {
|
||||
@@ -155,10 +159,7 @@ const SettingsPage: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageHeader
|
||||
title="Pengaturan"
|
||||
subtitle="Site, kandang, lantai (IoT), dan siklus produksi"
|
||||
/>
|
||||
<PageHeader title="Pengaturan" subtitle="Site, kandang, lantai (IoT), dan siklus produksi" />
|
||||
{error && (
|
||||
<div className="mb-4">
|
||||
<Banner kind="error">{error}</Banner>
|
||||
@@ -284,9 +285,7 @@ const SettingsPage: React.FC = () => {
|
||||
type="button"
|
||||
className="border border-red-600 text-red-600 px-4 py-2 rounded-lg text-sm font-semibold hover:bg-red-50 disabled:opacity-50"
|
||||
disabled={
|
||||
saving ||
|
||||
!registerCode ||
|
||||
slugify(registerCode) === pusatRegistration.code
|
||||
saving || !registerCode || slugify(registerCode) === pusatRegistration.code
|
||||
}
|
||||
onClick={() => {
|
||||
const code = slugify(registerCode);
|
||||
@@ -438,7 +437,9 @@ const SettingsPage: React.FC = () => {
|
||||
type="button"
|
||||
className="text-xs text-red-500 hover:underline"
|
||||
onClick={() =>
|
||||
setFeedInButtonUrls((rows) => rows.filter((_, rowIndex) => rowIndex !== index))
|
||||
setFeedInButtonUrls((rows) =>
|
||||
rows.filter((_, rowIndex) => rowIndex !== index)
|
||||
)
|
||||
}
|
||||
>
|
||||
Hapus URL
|
||||
@@ -510,42 +511,46 @@ const SettingsPage: React.FC = () => {
|
||||
{flocks.map((flock) => {
|
||||
const webhookUrl = `${window.location.origin}/api/v1/panel-iot/push/${flock.id}/`;
|
||||
return (
|
||||
<li key={flock.id} className="py-3 space-y-2">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<span className="text-sm font-medium text-gray-700">{flock.flock_name}</span>
|
||||
{isSuperAdmin && (
|
||||
<button
|
||||
type="button"
|
||||
className="text-xs text-red-500 hover:underline"
|
||||
onClick={() =>
|
||||
void wrap(async () => {
|
||||
await api.flocks.remove(flock.id);
|
||||
flash('Lantai dihapus.');
|
||||
})
|
||||
}
|
||||
>
|
||||
Hapus
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<label className="block text-xs text-gray-500">Webhook URL (IoT Panel → Dashboard)</label>
|
||||
<div className="flex items-center gap-2 rounded-lg bg-gray-50 border border-gray-200 px-3 py-2">
|
||||
<code className="flex-1 text-xs text-gray-700 break-all font-mono">{webhookUrl}</code>
|
||||
<button
|
||||
type="button"
|
||||
className="shrink-0 text-xs text-red-600 hover:underline font-semibold"
|
||||
onClick={() => {
|
||||
void navigator.clipboard.writeText(webhookUrl);
|
||||
flash('Webhook URL disalin.');
|
||||
}}
|
||||
>
|
||||
Salin
|
||||
</button>
|
||||
<li key={flock.id} className="py-3 space-y-2">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<span className="text-sm font-medium text-gray-700">{flock.flock_name}</span>
|
||||
{isSuperAdmin && (
|
||||
<button
|
||||
type="button"
|
||||
className="text-xs text-red-500 hover:underline"
|
||||
onClick={() =>
|
||||
void wrap(async () => {
|
||||
await api.flocks.remove(flock.id);
|
||||
flash('Lantai dihapus.');
|
||||
})
|
||||
}
|
||||
>
|
||||
Hapus
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
<div className="space-y-1">
|
||||
<label className="block text-xs text-gray-500">
|
||||
Webhook URL (IoT Panel → Dashboard)
|
||||
</label>
|
||||
<div className="flex items-center gap-2 rounded-lg bg-gray-50 border border-gray-200 px-3 py-2">
|
||||
<code className="flex-1 text-xs text-gray-700 break-all font-mono">
|
||||
{webhookUrl}
|
||||
</code>
|
||||
<button
|
||||
type="button"
|
||||
className="shrink-0 text-xs text-red-600 hover:underline font-semibold"
|
||||
onClick={() => {
|
||||
void navigator.clipboard.writeText(webhookUrl);
|
||||
flash('Webhook URL disalin.');
|
||||
}}
|
||||
>
|
||||
Salin
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
@@ -638,42 +643,42 @@ const SettingsPage: React.FC = () => {
|
||||
{cycles.map((cycle) => {
|
||||
const status = resolveCycleStatus(cycle);
|
||||
return (
|
||||
<li key={cycle.id} className="py-2 text-sm text-gray-700">
|
||||
<div className="flex justify-between gap-2">
|
||||
<button className="text-left" onClick={() => setSelectedCycleId(cycle.id)}>
|
||||
{formatDateId(cycle.start_date)}
|
||||
{cycle.end_date ? ` – ${formatDateId(cycle.end_date)}` : ''}
|
||||
<span className="block text-xs text-gray-400">
|
||||
{statusLabel(cycle)}
|
||||
{status === 'pending_close' && cycle.proposed_end_date
|
||||
? ` · usulan tutup ${formatDateId(cycle.proposed_end_date)}`
|
||||
: ''}{' '}
|
||||
· {cycle.doc_in_count} ekor · {cycle.chick_in_weight} g
|
||||
</span>
|
||||
</button>
|
||||
<div className="flex flex-col items-end gap-1">
|
||||
<button
|
||||
className="text-xs text-blue-600 hover:underline"
|
||||
onClick={() => beginCycleEdit(cycle)}
|
||||
>
|
||||
Ubah
|
||||
</button>
|
||||
<button
|
||||
className="text-xs text-red-500 hover:underline"
|
||||
onClick={() =>
|
||||
void wrap(async () => {
|
||||
if (editingCycleId === cycle.id) resetCycleForm();
|
||||
await api.cycles.remove(cycle.id);
|
||||
flash('Siklus dihapus.');
|
||||
})
|
||||
}
|
||||
>
|
||||
Hapus
|
||||
<li key={cycle.id} className="py-2 text-sm text-gray-700">
|
||||
<div className="flex justify-between gap-2">
|
||||
<button className="text-left" onClick={() => setSelectedCycleId(cycle.id)}>
|
||||
{formatDateId(cycle.start_date)}
|
||||
{cycle.end_date ? ` – ${formatDateId(cycle.end_date)}` : ''}
|
||||
<span className="block text-xs text-gray-400">
|
||||
{statusLabel(cycle)}
|
||||
{status === 'pending_close' && cycle.proposed_end_date
|
||||
? ` · usulan tutup ${formatDateId(cycle.proposed_end_date)}`
|
||||
: ''}{' '}
|
||||
· {cycle.doc_in_count} ekor · {cycle.chick_in_weight} g
|
||||
</span>
|
||||
</button>
|
||||
<div className="flex flex-col items-end gap-1">
|
||||
<button
|
||||
className="text-xs text-blue-600 hover:underline"
|
||||
onClick={() => beginCycleEdit(cycle)}
|
||||
>
|
||||
Ubah
|
||||
</button>
|
||||
<button
|
||||
className="text-xs text-red-500 hover:underline"
|
||||
onClick={() =>
|
||||
void wrap(async () => {
|
||||
if (editingCycleId === cycle.id) resetCycleForm();
|
||||
await api.cycles.remove(cycle.id);
|
||||
flash('Siklus dihapus.');
|
||||
})
|
||||
}
|
||||
>
|
||||
Hapus
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</section>
|
||||
@@ -681,9 +686,7 @@ const SettingsPage: React.FC = () => {
|
||||
|
||||
<ConfirmDialog
|
||||
open={pusatConfirm != null}
|
||||
title={
|
||||
pusatConfirm?.kind === 'change-code' ? 'Ubah kode pusat?' : 'Daftar ke pusat?'
|
||||
}
|
||||
title={pusatConfirm?.kind === 'change-code' ? 'Ubah kode pusat?' : 'Daftar ke pusat?'}
|
||||
message={
|
||||
pusatConfirm?.kind === 'change-code' ? (
|
||||
<p>
|
||||
@@ -698,9 +701,7 @@ const SettingsPage: React.FC = () => {
|
||||
</p>
|
||||
) : null
|
||||
}
|
||||
confirmLabel={
|
||||
pusatConfirm?.kind === 'change-code' ? 'Ya, ubah kode' : 'Ya, daftar'
|
||||
}
|
||||
confirmLabel={pusatConfirm?.kind === 'change-code' ? 'Ya, ubah kode' : 'Ya, daftar'}
|
||||
busy={saving}
|
||||
onCancel={() => {
|
||||
if (!saving) setPusatConfirm(null);
|
||||
|
||||
Reference in new issue
Block a user