fix initial balance logic and add edit for cycle data
This commit is contained in:
1 parent
7a9d600aa6
commit
697feb206c
15 files changed
+402
-93
No files matched your search
+106
-33
@@ -2,12 +2,27 @@ import React, { useState } from 'react';
|
||||
import { api, errorMessage } from '../services/apiClient.ts';
|
||||
import { useAuth } from '../context/AuthContext.tsx';
|
||||
import { useFarm } from '../context/FarmContext.tsx';
|
||||
import type { Cycle } from '../types/api.ts';
|
||||
import { Banner, PageHeader } from './ui/Feedback.tsx';
|
||||
import { addDaysIso, formatDateId, inclusiveDayCount, todayIso } from '../utils/format.ts';
|
||||
import { addDaysIso, formatDateId, inclusiveDayCount, resolveCycleStatus, todayIso } from '../utils/format.ts';
|
||||
|
||||
const inputClass =
|
||||
'border border-gray-300 rounded-md px-3 py-2 text-sm w-full focus:ring-red-500 focus:border-red-500';
|
||||
|
||||
const defaultCycleForm = () => ({
|
||||
startDate: todayIso(),
|
||||
endDate: addDaysIso(todayIso(), 34),
|
||||
docInCount: 20000,
|
||||
chickInWeight: 35,
|
||||
});
|
||||
|
||||
const cycleToForm = (cycle: Cycle) => ({
|
||||
startDate: cycle.start_date,
|
||||
endDate: cycle.end_date,
|
||||
docInCount: cycle.doc_in_count,
|
||||
chickInWeight: cycle.chick_in_weight,
|
||||
});
|
||||
|
||||
const SettingsPage: React.FC = () => {
|
||||
const { user } = useAuth();
|
||||
const {
|
||||
@@ -28,10 +43,39 @@ const SettingsPage: React.FC = () => {
|
||||
const [siteName, setSiteName] = useState('');
|
||||
const [kandangName, setKandangName] = useState('');
|
||||
const [flockName, setFlockName] = useState('');
|
||||
const [startDate, setStartDate] = useState(todayIso());
|
||||
const [endDate, setEndDate] = useState(() => addDaysIso(todayIso(), 34));
|
||||
const [docInCount, setDocInCount] = useState(20000);
|
||||
const [chickInWeight, setChickInWeight] = useState(35);
|
||||
const [editingCycleId, setEditingCycleId] = useState<number | null>(null);
|
||||
const [startDate, setStartDate] = useState(defaultCycleForm().startDate);
|
||||
const [endDate, setEndDate] = useState(defaultCycleForm().endDate);
|
||||
const [docInCount, setDocInCount] = useState(defaultCycleForm().docInCount);
|
||||
const [chickInWeight, setChickInWeight] = useState(defaultCycleForm().chickInWeight);
|
||||
|
||||
const resetCycleForm = () => {
|
||||
const defaults = defaultCycleForm();
|
||||
setEditingCycleId(null);
|
||||
setStartDate(defaults.startDate);
|
||||
setEndDate(defaults.endDate);
|
||||
setDocInCount(defaults.docInCount);
|
||||
setChickInWeight(defaults.chickInWeight);
|
||||
};
|
||||
|
||||
const beginCycleEdit = (cycle: Cycle) => {
|
||||
const form = cycleToForm(cycle);
|
||||
setEditingCycleId(cycle.id);
|
||||
setStartDate(form.startDate);
|
||||
setEndDate(form.endDate);
|
||||
setDocInCount(form.docInCount);
|
||||
setChickInWeight(form.chickInWeight);
|
||||
setSelectedCycleId(cycle.id);
|
||||
};
|
||||
|
||||
const buildCyclePayload = () => ({
|
||||
kandang: selectedKandangId!,
|
||||
total_days: inclusiveDayCount(startDate, endDate),
|
||||
start_date: startDate,
|
||||
end_date: endDate,
|
||||
chick_in_weight: chickInWeight,
|
||||
doc_in_count: docInCount,
|
||||
});
|
||||
|
||||
const flash = (msg: string) => {
|
||||
setNotice(msg);
|
||||
@@ -262,19 +306,24 @@ const SettingsPage: React.FC = () => {
|
||||
e.preventDefault();
|
||||
if (!selectedKandangId) return;
|
||||
void wrap(async () => {
|
||||
await api.cycles.create({
|
||||
kandang: selectedKandangId,
|
||||
total_days: inclusiveDayCount(startDate, endDate),
|
||||
current_day: 1,
|
||||
start_date: startDate,
|
||||
end_date: endDate,
|
||||
chick_in_weight: chickInWeight,
|
||||
doc_in_count: docInCount,
|
||||
});
|
||||
flash('Siklus dibuat.');
|
||||
const payload = buildCyclePayload();
|
||||
if (editingCycleId) {
|
||||
await api.cycles.update(editingCycleId, payload);
|
||||
flash('Siklus diperbarui.');
|
||||
resetCycleForm();
|
||||
} else {
|
||||
await api.cycles.create({ ...payload, current_day: 1 });
|
||||
flash('Siklus dibuat.');
|
||||
resetCycleForm();
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
{editingCycleId && (
|
||||
<p className="text-xs font-medium text-blue-700 bg-blue-50 border border-blue-100 rounded-md px-3 py-2">
|
||||
Mengedit siklus {formatDateId(startDate)} – {formatDateId(endDate)}
|
||||
</p>
|
||||
)}
|
||||
<label className="block text-sm">
|
||||
Mulai
|
||||
<input
|
||||
@@ -322,12 +371,24 @@ const SettingsPage: React.FC = () => {
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
<button
|
||||
className="bg-red-600 text-white px-3 py-2 rounded-lg text-sm font-semibold"
|
||||
disabled={saving}
|
||||
>
|
||||
Tambah siklus
|
||||
</button>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
className="bg-red-600 text-white px-3 py-2 rounded-lg text-sm font-semibold"
|
||||
disabled={saving}
|
||||
>
|
||||
{editingCycleId ? 'Simpan perubahan' : 'Tambah siklus'}
|
||||
</button>
|
||||
{editingCycleId && (
|
||||
<button
|
||||
type="button"
|
||||
className="border border-gray-300 text-gray-700 px-3 py-2 rounded-lg text-sm font-semibold hover:bg-gray-50"
|
||||
disabled={saving}
|
||||
onClick={resetCycleForm}
|
||||
>
|
||||
Batal
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
<ul className="divide-y">
|
||||
{cycles.map((cycle) => (
|
||||
@@ -335,19 +396,31 @@ const SettingsPage: React.FC = () => {
|
||||
<div className="flex justify-between gap-2">
|
||||
<button className="text-left" onClick={() => setSelectedCycleId(cycle.id)}>
|
||||
{formatDateId(cycle.start_date)} – {formatDateId(cycle.end_date)}
|
||||
<span className="block text-xs text-gray-400">{cycle.status}</span>
|
||||
</button>
|
||||
<button
|
||||
className="text-xs text-red-500 hover:underline"
|
||||
onClick={() =>
|
||||
void wrap(async () => {
|
||||
await api.cycles.remove(cycle.id);
|
||||
flash('Siklus dihapus.');
|
||||
})
|
||||
}
|
||||
>
|
||||
Hapus
|
||||
<span className="block text-xs text-gray-400">
|
||||
{resolveCycleStatus(cycle) === 'active' ? 'Aktif' : 'Selesai'} ·{' '}
|
||||
{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>
|
||||
</li>
|
||||
))}
|
||||
|
||||
Reference in new issue
Block a user