feat: set default value for date, kandang ID, and category from url query

This commit is contained in:
ValdiANS
2026-01-21 12:37:53 +07:00
parent 248d4f75d8
commit 0dbcb83c54
@@ -41,6 +41,7 @@ import { PhaseActivity } from '@/types/api/daily-checklist/phase-activity';
import DebouncedTextArea from '@/components/input/DebouncedTextArea'; import DebouncedTextArea from '@/components/input/DebouncedTextArea';
import DropFileInput from '@/components/input/DropFileInput'; import DropFileInput from '@/components/input/DropFileInput';
import Link from 'next/link'; import Link from 'next/link';
import { useRouter, useSearchParams, usePathname } from 'next/navigation';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
// Static categories // Static categories
@@ -67,7 +68,23 @@ interface Phase {
} }
export function DailyChecklistContent() { export function DailyChecklistContent() {
const [kandangId, setKandangId] = useState(''); const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const [kandangId, setKandangId] = useState(
searchParams.get('kandang_id') || ''
);
const [date, setDate] = useState(() => {
const paramDate = searchParams.get('date');
if (paramDate) return paramDate;
const today = new Date();
return today.toISOString().split('T')[0];
});
const [selectedCategory, setSelectedCategory] = useState(
searchParams.get('category') || ''
);
const { options: kandangOptions, isLoadingOptions: isLoadingKandangs } = const { options: kandangOptions, isLoadingOptions: isLoadingKandangs } =
useSelect(KandangApi.basePath, 'id', 'name', 'search', { useSelect(KandangApi.basePath, 'id', 'name', 'search', {
@@ -104,12 +121,6 @@ export function DailyChecklistContent() {
? employeesRes.data || [] ? employeesRes.data || []
: []; : [];
const [date, setDate] = useState(() => {
const today = new Date();
return today.toISOString().split('T')[0];
});
const [selectedCategory, setSelectedCategory] = useState('');
const [selectedPhaseIds, setSelectedPhaseIds] = useState<string[]>([]); const [selectedPhaseIds, setSelectedPhaseIds] = useState<string[]>([]);
const [selectedEmployees, setSelectedEmployees] = useState< const [selectedEmployees, setSelectedEmployees] = useState<
@@ -118,7 +129,7 @@ export function DailyChecklistContent() {
const [dailyChecklistId, setDailyChecklistId] = useState<string | null>(null); const [dailyChecklistId, setDailyChecklistId] = useState<string | null>(null);
const [checklistStatus, setChecklistStatus] = useState<string>('DRAFT'); const [checklistStatus, setChecklistStatus] = useState<string>('DRAFT');
const [isEditMode, setIsEditMode] = useState(false); // const [isEditMode, setIsEditMode] = useState(false);
// Activities grouped by phase // Activities grouped by phase
const [activitiesByPhase, setActivitiesByPhase] = useState<{ const [activitiesByPhase, setActivitiesByPhase] = useState<{
@@ -148,13 +159,57 @@ export function DailyChecklistContent() {
const [searchAbk, setSearchAbk] = useState(''); const [searchAbk, setSearchAbk] = useState('');
const [searchPhase, setSearchPhase] = useState(''); const [searchPhase, setSearchPhase] = useState('');
const [loading, setLoading] = useState(false); const [isLoadingSubmit, setIsLoadingSubmit] = useState(false);
const [isLoadingDraft, setIsLoadingDraft] = useState(false);
const [initialLoading, setInitialLoading] = useState(true); const [initialLoading, setInitialLoading] = useState(true);
const [existingDocuments, setExistingDocuments] = useState<Document[]>([]); const [existingDocuments, setExistingDocuments] = useState<Document[]>([]);
const [documents, setDocuments] = useState<File[]>([]); const [documents, setDocuments] = useState<File[]>([]);
const [deletedDocumentIds, setDeletedDocumentIds] = useState<number[]>([]); const [deletedDocumentIds, setDeletedDocumentIds] = useState<number[]>([]);
// Sync state to URL query params
useEffect(() => {
const params = new URLSearchParams(searchParams.toString());
let pendingUpdate = false;
// Sync date
if (date) {
if (params.get('date') !== date) {
params.set('date', date);
pendingUpdate = true;
}
} else if (params.has('date')) {
params.delete('date');
pendingUpdate = true;
}
// Sync kandang_id
if (kandangId) {
if (params.get('kandang_id') !== kandangId) {
params.set('kandang_id', kandangId);
pendingUpdate = true;
}
} else if (params.has('kandang_id')) {
params.delete('kandang_id');
pendingUpdate = true;
}
// Sync category
if (selectedCategory) {
if (params.get('category') !== selectedCategory) {
params.set('category', selectedCategory);
pendingUpdate = true;
}
} else if (params.has('category')) {
params.delete('category');
pendingUpdate = true;
}
if (pendingUpdate) {
router.replace(`${pathname}?${params.toString()}`);
}
}, [date, kandangId, selectedCategory, pathname, router, searchParams]);
// Format date for display // Format date for display
const formatDateForDisplay = (dateStr: string) => { const formatDateForDisplay = (dateStr: string) => {
if (!dateStr) return 'Pilih tanggal'; if (!dateStr) return 'Pilih tanggal';
@@ -179,7 +234,7 @@ export function DailyChecklistContent() {
if (!date || !kandangId || !selectedCategory) { if (!date || !kandangId || !selectedCategory) {
setDailyChecklistId(null); setDailyChecklistId(null);
setChecklistStatus('DRAFT'); setChecklistStatus('DRAFT');
setIsEditMode(false); // setIsEditMode(false);
setSelectedPhaseIds([]); setSelectedPhaseIds([]);
setActivitiesByPhase({}); setActivitiesByPhase({});
setTaskIdsByPhaseActivityId({}); setTaskIdsByPhaseActivityId({});
@@ -216,7 +271,7 @@ export function DailyChecklistContent() {
existingPhases.data.phases.length > 0 existingPhases.data.phases.length > 0
) { ) {
// Existing checklist - EDIT MODE // Existing checklist - EDIT MODE
setIsEditMode(true); // setIsEditMode(true);
const phaseIds = existingPhases.data.phases.map((p) => const phaseIds = existingPhases.data.phases.map((p) =>
String(p.phase_id) String(p.phase_id)
); );
@@ -234,7 +289,7 @@ export function DailyChecklistContent() {
} }
} else { } else {
// New checklist - CREATE MODE // New checklist - CREATE MODE
setIsEditMode(false); // setIsEditMode(false);
setSelectedPhaseIds([]); setSelectedPhaseIds([]);
} }
} catch (error) { } catch (error) {
@@ -608,7 +663,7 @@ export function DailyChecklistContent() {
// taskId, // taskId,
// hasTaskId: !!taskId, // hasTaskId: !!taskId,
// checklistStatus, // checklistStatus,
// isEditable, // isChecklistStatusDraft,
// }); // });
if (!taskId) { if (!taskId) {
@@ -618,7 +673,7 @@ export function DailyChecklistContent() {
return; return;
} }
if (!isEditable) { if (!isChecklistStatusDraft) {
console.warn( console.warn(
'[CHECKBOX] Checklist is not editable, status:', '[CHECKBOX] Checklist is not editable, status:',
checklistStatus checklistStatus
@@ -736,7 +791,7 @@ export function DailyChecklistContent() {
return; return;
} }
setLoading(true); setIsLoadingSubmit(true);
try { try {
const submitRes = await DailyChecklistApi.submit( const submitRes = await DailyChecklistApi.submit(
@@ -757,13 +812,15 @@ export function DailyChecklistContent() {
console.error('Error submitting:', error); console.error('Error submitting:', error);
toast.error('Terjadi kesalahan'); toast.error('Terjadi kesalahan');
} finally { } finally {
setLoading(false); setIsLoadingSubmit(false);
} }
}; };
const handleSaveDraft = async () => { const handleSaveDraft = async () => {
if (!dailyChecklistId) return; if (!dailyChecklistId) return;
setIsLoadingDraft(true);
const uploadImageRes = await DailyChecklistApi.uploadImage( const uploadImageRes = await DailyChecklistApi.uploadImage(
Number(dailyChecklistId), Number(dailyChecklistId),
'DRAFT', 'DRAFT',
@@ -774,9 +831,11 @@ export function DailyChecklistContent() {
if (isResponseError(uploadImageRes)) { if (isResponseError(uploadImageRes)) {
console.error('Error saving draft:', uploadImageRes.message); console.error('Error saving draft:', uploadImageRes.message);
toast.error('Gagal menyimpan draft'); toast.error('Gagal menyimpan draft');
setIsLoadingDraft(false);
return; return;
} }
setIsLoadingDraft(false);
toast.success('Draft tersimpan!'); toast.success('Draft tersimpan!');
}; };
@@ -838,7 +897,7 @@ export function DailyChecklistContent() {
return grouped; return grouped;
}; };
const isEditable = checklistStatus === 'DRAFT'; const isChecklistStatusDraft = checklistStatus === 'DRAFT';
if (initialLoading) { if (initialLoading) {
return ( return (
@@ -871,7 +930,7 @@ export function DailyChecklistContent() {
<h1 className='text-2xl font-semibold text-gray-900'> <h1 className='text-2xl font-semibold text-gray-900'>
Daily Checklist Daily Checklist
</h1> </h1>
{isEditMode && ( {isChecklistStatusDraft && (
<Badge <Badge
variant='outline' variant='outline'
className='border-amber-300 text-amber-700 bg-white' className='border-amber-300 text-amber-700 bg-white'
@@ -907,7 +966,7 @@ export function DailyChecklistContent() {
<DatePicker <DatePicker
date={date} date={date}
onDateChange={setDate} onDateChange={setDate}
disabled={!isEditable} disabled={!isChecklistStatusDraft}
placeholder='Pilih tanggal' placeholder='Pilih tanggal'
formatDisplay={formatDateForDisplay} formatDisplay={formatDateForDisplay}
/> />
@@ -921,7 +980,7 @@ export function DailyChecklistContent() {
<Select <Select
value={kandangId} value={kandangId}
onValueChange={setKandangId} onValueChange={setKandangId}
disabled={!isEditable} disabled={!isChecklistStatusDraft}
> >
<SelectTrigger <SelectTrigger
id='kandang' id='kandang'
@@ -949,7 +1008,7 @@ export function DailyChecklistContent() {
<Select <Select
value={selectedCategory} value={selectedCategory}
onValueChange={setSelectedCategory} onValueChange={setSelectedCategory}
disabled={!isEditable} disabled={!isChecklistStatusDraft}
> >
<SelectTrigger <SelectTrigger
id='category' id='category'
@@ -971,19 +1030,21 @@ export function DailyChecklistContent() {
{/* Phase Selection Section */} {/* Phase Selection Section */}
{dailyChecklistId && ( {dailyChecklistId && (
<div className='mb-6 pb-6 border-b border-gray-200'> <div className='mb-6 pb-6 border-b border-gray-200'>
<div className='flex items-center justify-between mb-3'> {isChecklistStatusDraft && (
<Label>Fase / Tahap</Label> <div className='flex items-center justify-between mb-3'>
<Button <Label>Fase / Tahap</Label>
onClick={handleAddPhase} <Button
size='sm' onClick={handleAddPhase}
variant='outline' size='sm'
className='border-[#0069e0] text-[#0069e0] hover:bg-blue-50' variant='outline'
disabled={!selectedCategory || !isEditable} className='border-[#0069e0] text-[#0069e0] hover:bg-blue-50'
> disabled={!selectedCategory || !isChecklistStatusDraft}
<Plus className='w-4 h-4 mr-1' /> >
Pilih Fase <Plus className='w-4 h-4 mr-1' />
</Button> Pilih Fase
</div> </Button>
</div>
)}
{selectedPhaseIds.length > 0 ? ( {selectedPhaseIds.length > 0 ? (
<div className='flex flex-wrap gap-2'> <div className='flex flex-wrap gap-2'>
@@ -1010,19 +1071,21 @@ export function DailyChecklistContent() {
{/* ABK Assignment Section */} {/* ABK Assignment Section */}
{dailyChecklistId && selectedPhaseIds.length > 0 && ( {dailyChecklistId && selectedPhaseIds.length > 0 && (
<div className='mb-6 pb-6 border-b border-gray-200'> <div className='mb-6 pb-6 border-b border-gray-200'>
<div className='flex items-center justify-between mb-3'> {isChecklistStatusDraft && (
<Label>ABK Assignment</Label> <div className='flex items-center justify-between mb-3'>
<Button <Label>ABK Assignment</Label>
onClick={handleAddAbk} <Button
size='sm' onClick={handleAddAbk}
variant='outline' size='sm'
className='border-[#0069e0] text-[#0069e0] hover:bg-blue-50' variant='outline'
disabled={!kandangId || !isEditable} className='border-[#0069e0] text-[#0069e0] hover:bg-blue-50'
> disabled={!kandangId || !isChecklistStatusDraft}
<Plus className='w-4 h-4 mr-1' /> >
Tambah ABK <Plus className='w-4 h-4 mr-1' />
</Button> Tambah ABK
</div> </Button>
</div>
)}
{selectedEmployees.length > 0 ? ( {selectedEmployees.length > 0 ? (
<div className='flex flex-wrap gap-2'> <div className='flex flex-wrap gap-2'>
@@ -1033,7 +1096,7 @@ export function DailyChecklistContent() {
className='px-3 py-1.5 bg-gray-100 text-gray-700 border border-gray-200 rounded-lg' className='px-3 py-1.5 bg-gray-100 text-gray-700 border border-gray-200 rounded-lg'
> >
{emp.name} {emp.name}
{isEditable && ( {isChecklistStatusDraft && (
<button <button
onClick={() => handleRemoveAbk(String(emp.id))} onClick={() => handleRemoveAbk(String(emp.id))}
className='ml-2 hover:text-gray-900' className='ml-2 hover:text-gray-900'
@@ -1198,7 +1261,7 @@ export function DailyChecklistContent() {
e.target.checked e.target.checked
) )
} }
disabled={!isEditable} disabled={!isChecklistStatusDraft}
className='checkbox-clean' className='checkbox-clean'
/> />
</td> </td>
@@ -1225,7 +1288,7 @@ export function DailyChecklistContent() {
); );
} }
}} }}
disabled={!isEditable} disabled={!isChecklistStatusDraft}
/> />
</td> </td>
</tr> </tr>
@@ -1321,61 +1384,68 @@ export function DailyChecklistContent() {
/> />
</Link> </Link>
<Button {isChecklistStatusDraft && (
type='button' <Button
variant='ghost' type='button'
color='error' variant='ghost'
onClick={() => { color='error'
setDeletedDocumentIds((prevIds) => [ onClick={() => {
...prevIds, setDeletedDocumentIds((prevIds) => [
existingDocument.id, ...prevIds,
]); existingDocument.id,
]);
setExistingDocuments((prevExistingDocument) => { setExistingDocuments(
const newExistingDocuments = [ (prevExistingDocument) => {
...prevExistingDocument, const newExistingDocuments = [
]; ...prevExistingDocument,
newExistingDocuments.splice( ];
existingDocumentIdx, newExistingDocuments.splice(
1 existingDocumentIdx,
1
);
return newExistingDocuments;
}
); );
return newExistingDocuments; }}
}); className='p-1 rounded-full text-error focus-visible:text-error-content hover:text-error-content'
}} >
className='p-1 rounded-full text-error focus-visible:text-error-content hover:text-error-content' <Icon
> icon='fluent:delete-12-regular'
<Icon width={20}
icon='fluent:delete-12-regular' height={20}
width={20} />
height={20} </Button>
/> )}
</Button>
</div> </div>
) )
)} )}
</div> </div>
)} )}
<DropFileInput {isChecklistStatusDraft && (
name='Dokumen' <DropFileInput
label='Dokumen' name='Dokumen'
values={documents} label='Dokumen'
onChange={(files) => { values={documents}
setDocuments(files); onChange={(files) => {
}} setDocuments(files);
onDelete={(deletedFileIdx: number) => { }}
const newRequestDocuments = [...documents]; onDelete={(deletedFileIdx: number) => {
const newRequestDocuments = [...documents];
newRequestDocuments?.splice(deletedFileIdx, 1); newRequestDocuments?.splice(deletedFileIdx, 1);
setDocuments(newRequestDocuments); setDocuments(newRequestDocuments);
}} }}
className={{ disabled={!isChecklistStatusDraft}
wrapper: 'mt-6', className={{
inputWrapper: 'flex items-center', wrapper: 'mt-6',
label: 'font-semibold text-gray-900', inputWrapper: 'flex items-center',
}} label: 'font-semibold text-gray-900',
/> }}
/>
)}
</> </>
)} )}
@@ -1383,24 +1453,30 @@ export function DailyChecklistContent() {
{dailyChecklistId && {dailyChecklistId &&
selectedPhaseIds.length > 0 && selectedPhaseIds.length > 0 &&
selectedEmployees.length > 0 && selectedEmployees.length > 0 &&
isEditable && ( isChecklistStatusDraft && (
<div className='flex justify-end gap-3 mt-6 pt-6 border-t border-gray-200'> <div className='flex justify-end gap-3 mt-6 pt-6 border-t border-gray-200'>
<Button <Button
onClick={handleSaveDraft} onClick={handleSaveDraft}
variant='outline' variant='outline'
disabled={loading} disabled={isLoadingDraft}
className='border-gray-200' className='border-gray-200'
> >
<Save className='w-4 h-4 mr-2' /> <Save className='w-4 h-4 mr-2' />
Simpan Draft {isLoadingDraft ? (
<span className='loading loading-spinner loading-sm' />
) : (
'Simpan Draft'
)}
</Button> </Button>
<Button <Button
onClick={handleSubmit} onClick={handleSubmit}
disabled={loading} disabled={isLoadingSubmit}
className='bg-[#0069e0] hover:bg-[#0052b3] text-white' className='bg-[#0069e0] hover:bg-[#0052b3] text-white'
> >
<Send className='w-4 h-4 mr-2' /> <Send className='w-4 h-4 mr-2' />
Submit Checklist {isLoadingSubmit
? 'Mengirim Checklist...'
: 'Submit Checklist'}
</Button> </Button>
</div> </div>
)} )}