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