fix: hide phase selection, abk assignment, and activity checklist form when kandang is empty

This commit is contained in:
ValdiANS
2026-04-23 16:22:05 +07:00
parent c487e7f53e
commit 4d01ad7d1d
@@ -98,6 +98,8 @@ export function DailyChecklistContent() {
const [emptyKandang, setEmptyKandang] = useState(false); const [emptyKandang, setEmptyKandang] = useState(false);
const [emptyKandangEndDate, setEmptyKandangEndDate] = useState(''); const [emptyKandangEndDate, setEmptyKandangEndDate] = useState('');
const isKandangEmpty = selectedCategory === 'empty_kandang';
const { const {
options: kandangOptions, options: kandangOptions,
isLoadingMore: isLoadingMoreKandang, isLoadingMore: isLoadingMoreKandang,
@@ -298,7 +300,7 @@ export function DailyChecklistContent() {
if (isResponseError(checklist)) { if (isResponseError(checklist)) {
console.error('Error upserting checklist:', checklist.message); console.error('Error upserting checklist:', checklist.message);
toast.error('Gagal memuat checklist'); toast.error('Gagal memuat checklist: ' + checklist.message);
return; return;
} }
@@ -311,6 +313,12 @@ export function DailyChecklistContent() {
if (isResponseError(existingPhases)) { if (isResponseError(existingPhases)) {
console.error('Error loading phases:', existingPhases.message); console.error('Error loading phases:', existingPhases.message);
} else if (
existingPhases &&
existingPhases.data &&
existingPhases.data.phases.length === 0
) {
toast.success('Berhasil membuat daily checklist!');
} else if ( } else if (
existingPhases && existingPhases &&
existingPhases.data && existingPhases.data &&
@@ -1118,7 +1126,7 @@ export function DailyChecklistContent() {
</div> </div>
{/* Phase Selection Section */} {/* Phase Selection Section */}
{dailyChecklistId && ( {!isKandangEmpty && dailyChecklistId && (
<div className='mb-6 pb-6 border-b border-gray-200'> <div className='mb-6 pb-6 border-b border-gray-200'>
{isChecklistStatusDraft && ( {isChecklistStatusDraft && (
<div className='flex items-center justify-between mb-3'> <div className='flex items-center justify-between mb-3'>
@@ -1159,298 +1167,314 @@ export function DailyChecklistContent() {
)} )}
{/* ABK Assignment Section */} {/* ABK Assignment Section */}
{dailyChecklistId && selectedPhaseIds.length > 0 && ( {!isKandangEmpty &&
<div className='mb-6 pb-6 border-b border-gray-200'> dailyChecklistId &&
{isChecklistStatusDraft && ( selectedPhaseIds.length > 0 && (
<div className='flex items-center justify-between mb-3'> <div className='mb-6 pb-6 border-b border-gray-200'>
<Label>ABK Assignment</Label> {isChecklistStatusDraft && (
<Button <div className='flex items-center justify-between mb-3'>
onClick={handleAddAbk} <Label>ABK Assignment</Label>
size='sm' <Button
variant='outline' onClick={handleAddAbk}
className='border-[#0069e0] text-[#0069e0] hover:bg-blue-50' size='sm'
disabled={!kandangId || !isChecklistStatusDraft} variant='outline'
> className='border-[#0069e0] text-[#0069e0] hover:bg-blue-50'
<Plus className='w-4 h-4 mr-1' /> disabled={!kandangId || !isChecklistStatusDraft}
Tambah ABK
</Button>
</div>
)}
{selectedEmployees.length > 0 ? (
<div className='flex flex-wrap gap-2'>
{selectedEmployees.map((emp) => (
<Badge
key={emp.id}
variant='secondary'
className='px-3 py-1.5 bg-gray-100 text-gray-700 border border-gray-200 rounded-lg'
> >
{emp.name} <Plus className='w-4 h-4 mr-1' />
{isChecklistStatusDraft && ( Tambah ABK
<button </Button>
onClick={() => handleRemoveAbk(String(emp.id))} </div>
className='ml-2 hover:text-gray-900' )}
>
<X className='w-3 h-3' /> {selectedEmployees.length > 0 ? (
</button> <div className='flex flex-wrap gap-2'>
)} {selectedEmployees.map((emp) => (
</Badge> <Badge
))} key={emp.id}
</div> variant='secondary'
) : ( className='px-3 py-1.5 bg-gray-100 text-gray-700 border border-gray-200 rounded-lg'
<p className='text-sm text-gray-500'>Belum ada ABK dipilih</p> >
)} {emp.name}
</div> {isChecklistStatusDraft && (
)} <button
onClick={() => handleRemoveAbk(String(emp.id))}
className='ml-2 hover:text-gray-900'
>
<X className='w-3 h-3' />
</button>
)}
</Badge>
))}
</div>
) : (
<p className='text-sm text-gray-500'>
Belum ada ABK dipilih
</p>
)}
</div>
)}
{/* Activity Checklist Table */} {/* Activity Checklist Table */}
{dailyChecklistId && {!isKandangEmpty && (
selectedPhaseIds.length > 0 && <>
selectedEmployees.length > 0 ? ( {dailyChecklistId &&
<div> selectedPhaseIds.length > 0 &&
<h3 className='font-semibold text-gray-900 mb-4'> selectedEmployees.length > 0 ? (
Checklist Aktivitas <div>
</h3> <h3 className='font-semibold text-gray-900 mb-4'>
{Object.keys(activitiesByPhase).length > 0 ? ( Checklist Aktivitas
<div className='overflow-x-auto'> </h3>
<table className='w-full border border-gray-200 rounded-lg'> {Object.keys(activitiesByPhase).length > 0 ? (
<thead> <div className='overflow-x-auto'>
<tr className='bg-gray-50 border-b border-gray-200'> <table className='w-full border border-gray-200 rounded-lg'>
<th className='text-left py-3 px-4 text-sm font-semibold text-gray-700 border-r border-gray-200 min-w-[200px]'> <thead>
Aktivitas <tr className='bg-gray-50 border-b border-gray-200'>
</th> <th className='text-left py-3 px-4 text-sm font-semibold text-gray-700 border-r border-gray-200 min-w-[200px]'>
{sortedSelectedEmployees.map((emp) => ( Aktivitas
<th </th>
key={emp.id} {sortedSelectedEmployees.map((emp) => (
className='text-center py-3 px-4 text-sm font-semibold text-gray-700 border-r border-gray-200 min-w-[100px]' <th
> key={emp.id}
{emp.name} className='text-center py-3 px-4 text-sm font-semibold text-gray-700 border-r border-gray-200 min-w-[100px]'
</th>
))}
<th className='text-left py-3 px-4 text-sm font-semibold text-gray-700 min-w-[200px]'>
Catatan
</th>
</tr>
</thead>
<tbody>
{Object.keys(groupActivitiesByPhase()).flatMap(
(phaseId) => {
const phaseData = groupActivitiesByPhase()[phaseId];
const { phase, timeGroups } = phaseData;
const timeTypes = Object.keys(timeGroups).sort(
(a, b) =>
TIME_TYPE_ORDER.indexOf(a) -
TIME_TYPE_ORDER.indexOf(b)
);
// Count total activities in this phase
const totalActivities = timeTypes.reduce(
(sum, timeType) =>
sum + timeGroups[timeType].length,
0
);
// Build all rows for this phase
const rows = [];
// PHASE Header (Main parent) - BLUE
rows.push(
<tr
key={`phase-${phaseId}`}
className='bg-blue-50 border-b border-blue-200'
>
<td
colSpan={selectedEmployees.length + 2}
className='py-2.5 px-4'
> >
<div className='flex items-center gap-2'> {emp.name}
<span className='text-sm font-semibold text-blue-900'> </th>
{phase.name} ))}
</span> <th className='text-left py-3 px-4 text-sm font-semibold text-gray-700 min-w-[200px]'>
<Badge Catatan
variant='secondary' </th>
className='text-xs bg-blue-100 text-blue-700 border-blue-200 rounded-lg' </tr>
> </thead>
{totalActivities} aktivitas <tbody>
</Badge> {Object.keys(groupActivitiesByPhase()).flatMap(
</div> (phaseId) => {
</td> const phaseData =
</tr> groupActivitiesByPhase()[phaseId];
); const { phase, timeGroups } = phaseData;
// TIME_TYPE sub-headers and activities const timeTypes = Object.keys(timeGroups).sort(
timeTypes.forEach((timeType) => { (a, b) =>
const activities = timeGroups[timeType]; TIME_TYPE_ORDER.indexOf(a) -
const hasMultipleTimeTypes = timeTypes.length > 1; TIME_TYPE_ORDER.indexOf(b)
);
// TIME Header (optional, only if phase has multiple time types) - GRAY SOFT // Count total activities in this phase
if (hasMultipleTimeTypes) { const totalActivities = timeTypes.reduce(
(sum, timeType) =>
sum + timeGroups[timeType].length,
0
);
// Build all rows for this phase
const rows = [];
// PHASE Header (Main parent) - BLUE
rows.push( rows.push(
<tr <tr
key={`time-${phaseId}-${timeType}`} key={`phase-${phaseId}`}
className='bg-gray-50 border-b border-gray-200' className='bg-blue-50 border-b border-blue-200'
> >
<td <td
colSpan={selectedEmployees.length + 2} colSpan={selectedEmployees.length + 2}
className='py-2 px-4 pl-8' className='py-2.5 px-4'
> >
<span className='text-xs font-medium text-gray-600'> <div className='flex items-center gap-2'>
{TIME_TYPE_LABELS[timeType]} ( <span className='text-sm font-semibold text-blue-900'>
{activities.length} aktivitas) {phase.name}
</span> </span>
<Badge
variant='secondary'
className='text-xs bg-blue-100 text-blue-700 border-blue-200 rounded-lg'
>
{totalActivities} aktivitas
</Badge>
</div>
</td> </td>
</tr> </tr>
); );
}
// ACTIVITY rows (Child rows with checkboxes) // TIME_TYPE sub-headers and activities
activities.sort((a, b) => timeTypes.forEach((timeType) => {
a.name.localeCompare(b.name, undefined, { const activities = timeGroups[timeType];
sensitivity: 'base', const hasMultipleTimeTypes =
}) timeTypes.length > 1;
);
activities.forEach((activity, index) => { // TIME Header (optional, only if phase has multiple time types) - GRAY SOFT
const taskId = if (hasMultipleTimeTypes) {
taskIdsByPhaseActivityId[activity.id]; rows.push(
const indentClass = hasMultipleTimeTypes <tr
? 'pl-12' key={`time-${phaseId}-${timeType}`}
: 'pl-8'; className='bg-gray-50 border-b border-gray-200'
rows.push(
<tr
key={`activity-${activity.id}`}
className={
index % 2 === 0
? 'bg-white'
: 'bg-gray-50/50'
}
>
<td
className={`py-3 px-4 ${indentClass} border-r border-gray-200`}
>
<p className='text-sm text-gray-900'>
{activity.name}
</p>
{activity.description && (
<p className='text-xs text-gray-500 mt-0.5'>
{activity.description}
</p>
)}
</td>
{sortedSelectedEmployees.map((emp) => (
<td
key={emp.id}
className='text-center py-3 px-4 border-r border-gray-200'
> >
<input <td
type='checkbox' colSpan={selectedEmployees.length + 2}
checked={ className='py-2 px-4 pl-8'
assignments[taskId]?.[emp.id] >
?.checked || false <span className='text-xs font-medium text-gray-600'>
} {TIME_TYPE_LABELS[timeType]} (
onChange={(e) => {activities.length} aktivitas)
handleCheckboxChange( </span>
String(activity.id), </td>
String(emp.id), </tr>
e.target.checked );
) }
}
disabled={!isChecklistStatusDraft}
className='checkbox-clean'
/>
</td>
))}
<td className='py-3 px-4'>
<DebouncedTextArea
delay={500}
name='notes'
rows={1}
placeholder='Catatan (opsional)'
value={
taskId && selectedEmployees.length > 0
? assignments[taskId]?.[
selectedEmployees[0].id
]?.note || ''
: ''
}
onChange={(e) => {
if (selectedEmployees.length > 0) {
handleNoteChange(
String(activity.id),
String(selectedEmployees[0].id),
e.target.value
);
}
}}
disabled={!isChecklistStatusDraft}
/>
</td>
</tr>
);
});
});
return rows; // ACTIVITY rows (Child rows with checkboxes)
} activities.sort((a, b) =>
)} a.name.localeCompare(b.name, undefined, {
</tbody> sensitivity: 'base',
</table> })
);
activities.forEach((activity, index) => {
const taskId =
taskIdsByPhaseActivityId[activity.id];
const indentClass = hasMultipleTimeTypes
? 'pl-12'
: 'pl-8';
rows.push(
<tr
key={`activity-${activity.id}`}
className={
index % 2 === 0
? 'bg-white'
: 'bg-gray-50/50'
}
>
<td
className={`py-3 px-4 ${indentClass} border-r border-gray-200`}
>
<p className='text-sm text-gray-900'>
{activity.name}
</p>
{activity.description && (
<p className='text-xs text-gray-500 mt-0.5'>
{activity.description}
</p>
)}
</td>
{sortedSelectedEmployees.map((emp) => (
<td
key={emp.id}
className='text-center py-3 px-4 border-r border-gray-200'
>
<input
type='checkbox'
checked={
assignments[taskId]?.[emp.id]
?.checked || false
}
onChange={(e) =>
handleCheckboxChange(
String(activity.id),
String(emp.id),
e.target.checked
)
}
disabled={!isChecklistStatusDraft}
className='checkbox-clean'
/>
</td>
))}
<td className='py-3 px-4'>
<DebouncedTextArea
delay={500}
name='notes'
rows={1}
placeholder='Catatan (opsional)'
value={
taskId &&
selectedEmployees.length > 0
? assignments[taskId]?.[
selectedEmployees[0].id
]?.note || ''
: ''
}
onChange={(e) => {
if (
selectedEmployees.length > 0
) {
handleNoteChange(
String(activity.id),
String(
selectedEmployees[0].id
),
e.target.value
);
}
}}
disabled={!isChecklistStatusDraft}
/>
</td>
</tr>
);
});
});
return rows;
}
)}
</tbody>
</table>
</div>
) : (
<div className='flex flex-col items-center justify-center py-16 text-center'>
<ListChecks className='w-16 h-16 text-gray-300 mb-4' />
<h3 className='text-lg font-semibold text-gray-700 mb-2'>
Tidak Ada Aktivitas
</h3>
<p className='text-sm text-gray-500 max-w-md'>
Tidak ada aktivitas untuk fase yang dipilih. Silakan
tambahkan aktivitas di Master Aktivitas.
</p>
</div>
)}
</div> </div>
) : ( ) : (
<div className='flex flex-col items-center justify-center py-16 text-center'> <div className='flex flex-col items-center justify-center py-16 text-center'>
<ListChecks className='w-16 h-16 text-gray-300 mb-4' /> {!dailyChecklistId ? (
<h3 className='text-lg font-semibold text-gray-700 mb-2'> <div>
Tidak Ada Aktivitas <FilePlus className='w-16 h-16 text-gray-300 mb-4 mx-auto' />
</h3> <h3 className='text-lg font-semibold text-gray-700 mb-2'>
<p className='text-sm text-gray-500 max-w-md'> Mulai Checklist Baru
Tidak ada aktivitas untuk fase yang dipilih. Silakan </h3>
tambahkan aktivitas di Master Aktivitas. <p className='text-sm text-gray-500 max-w-md'>
</p> Pilih tanggal, kandang, dan kategori untuk memulai
checklist harian Anda.
</p>
</div>
) : selectedPhaseIds.length === 0 ? (
<div className='flex flex-col items-center text-center'>
<FilePlus className='w-16 h-16 text-gray-300 mb-4' />
<h3 className='text-lg font-semibold text-gray-700 mb-2'>
Pilih Fase / Tahap
</h3>
<p className='text-sm text-gray-500 max-w-md'>
Klik tombol {'"'}Pilih Fase{'"'} untuk memilih tahap
aktivitas yang akan dikerjakan.
</p>
</div>
) : (
<div>
<FilePlus className='w-16 h-16 text-gray-300 mb-4 mx-auto' />
<h3 className='text-lg font-semibold text-gray-700 mb-2'>
Pilih ABK
</h3>
<p className='text-sm text-gray-500 max-w-md'>
Klik tombol {'"'}Tambah ABK{'"'} untuk memilih pekerja
yang akan ditugaskan.
</p>
</div>
)}
</div> </div>
)} )}
</div> </>
) : (
<div className='flex flex-col items-center justify-center py-16 text-center'>
{!dailyChecklistId ? (
<div>
<FilePlus className='w-16 h-16 text-gray-300 mb-4 mx-auto' />
<h3 className='text-lg font-semibold text-gray-700 mb-2'>
Mulai Checklist Baru
</h3>
<p className='text-sm text-gray-500 max-w-md'>
Pilih tanggal, kandang, dan kategori untuk memulai
checklist harian Anda.
</p>
</div>
) : selectedPhaseIds.length === 0 ? (
<div className='flex flex-col items-center text-center'>
<FilePlus className='w-16 h-16 text-gray-300 mb-4' />
<h3 className='text-lg font-semibold text-gray-700 mb-2'>
Pilih Fase / Tahap
</h3>
<p className='text-sm text-gray-500 max-w-md'>
Klik tombol {'"'}Pilih Fase{'"'} untuk memilih tahap
aktivitas yang akan dikerjakan.
</p>
</div>
) : (
<div>
<FilePlus className='w-16 h-16 text-gray-300 mb-4 mx-auto' />
<h3 className='text-lg font-semibold text-gray-700 mb-2'>
Pilih ABK
</h3>
<p className='text-sm text-gray-500 max-w-md'>
Klik tombol {'"'}Tambah ABK{'"'} untuk memilih pekerja
yang akan ditugaskan.
</p>
</div>
)}
</div>
)} )}
{dailyChecklistId && {!isKandangEmpty &&
dailyChecklistId &&
selectedPhaseIds.length > 0 && selectedPhaseIds.length > 0 &&
selectedEmployees.length > 0 && ( selectedEmployees.length > 0 && (
<> <>
@@ -1548,7 +1572,8 @@ export function DailyChecklistContent() {
)} )}
{/* Action Buttons */} {/* Action Buttons */}
{dailyChecklistId && {!isKandangEmpty &&
dailyChecklistId &&
selectedPhaseIds.length > 0 && selectedPhaseIds.length > 0 &&
selectedEmployees.length > 0 && selectedEmployees.length > 0 &&
isChecklistStatusDraft && ( isChecklistStatusDraft && (