diff --git a/src/api/master-shift.jsx b/src/api/master-shift.jsx index a5a3f24..a4d7fee 100644 --- a/src/api/master-shift.jsx +++ b/src/api/master-shift.jsx @@ -6,9 +6,75 @@ const getAllShift = async (queryParams) => { method: 'get', prefix: `shift?${queryParams.toString()}`, }); - return response; + console.log('getAllShift response:', response); + console.log('Query params:', queryParams.toString()); + + // Check if response has error + if (response.error) { + console.error('getAllShift error response:', response); + return { + status: response.statusCode || 500, + data: { + data: [], + paging: { + page: 1, + limit: 10, + total: 0, + page_total: 0 + }, + total: 0 + }, + error: response.message + }; + } + + // Check if backend returns paginated data + if (response.paging) { + const totalData = response.data?.[0]?.total_data || response.rows || response.data?.length || 0; + + return { + status: response.statusCode || 200, + data: { + data: response.data || [], + paging: { + page: response.paging.current_page || 1, + limit: response.paging.current_limit || 10, + total: totalData, + page_total: response.paging.total_page || Math.ceil(totalData / (response.paging.current_limit || 10)) + }, + total: totalData + } + }; + } + + // Fallback: If backend returns all data without pagination + const params = Object.fromEntries(queryParams); + const currentPage = parseInt(params.page) || 1; + const currentLimit = parseInt(params.limit) || 10; + + const allData = response.data || []; + const totalData = allData.length; + + // Client-side pagination + const startIndex = (currentPage - 1) * currentLimit; + const endIndex = startIndex + currentLimit; + const paginatedData = allData.slice(startIndex, endIndex); + + return { + status: response.statusCode || 200, + data: { + data: paginatedData, + paging: { + page: currentPage, + limit: currentLimit, + total: totalData, + page_total: Math.ceil(totalData / currentLimit) + }, + total: totalData + } + }; } catch (error) { - console.error('getAllShift error:', error); + console.error('getAllShift catch error:', error); return { status: 500, data: { @@ -38,12 +104,27 @@ const createShift = async (queryParams) => { const response = await SendRequest({ method: 'post', prefix: `shift`, - data: queryParams, + params: queryParams, }); + console.log('createShift full response:', response); + console.log('createShift payload sent:', queryParams); + + // Check if response has error flag + if (response.error) { + return { + statusCode: response.statusCode || 500, + data: null, + message: response.message || 'Request failed', + rows: 0 + }; + } + + // Backend returns: { statusCode, message, rows, data: [shift_object] } return { statusCode: response.statusCode || 200, - data: response.data, - message: response.message + data: response.data?.[0] || response.data, + message: response.message, + rows: response.rows }; }; @@ -51,12 +132,27 @@ const updateShift = async (id, queryParams) => { const response = await SendRequest({ method: 'put', prefix: `shift/${id}`, - data: queryParams, + params: queryParams, }); + console.log('updateShift full response:', response); + console.log('updateShift payload sent:', queryParams); + + // Check if response has error flag + if (response.error) { + return { + statusCode: response.statusCode || 500, + data: null, + message: response.message || 'Request failed', + rows: 0 + }; + } + + // Backend returns: { statusCode, message, rows, data: [shift_object] } return { statusCode: response.statusCode || 200, - data: response.data, - message: response.message + data: response.data?.[0] || response.data, + message: response.message, + rows: response.rows }; }; @@ -65,10 +161,24 @@ const deleteShift = async (id) => { method: 'delete', prefix: `shift/${id}`, }); + console.log('deleteShift full response:', response); + + // Check if response has error flag + if (response.error) { + return { + statusCode: response.statusCode || 500, + data: null, + message: response.message || 'Request failed', + rows: 0 + }; + } + + // Backend returns: { statusCode, message, rows: null, data: true } return { statusCode: response.statusCode || 200, data: response.data, - message: response.message + message: response.message, + rows: response.rows }; }; diff --git a/src/pages/jadwalShift/component/ListJadwalShift.jsx b/src/pages/jadwalShift/component/ListJadwalShift.jsx index 23f5dae..631c3c9 100644 --- a/src/pages/jadwalShift/component/ListJadwalShift.jsx +++ b/src/pages/jadwalShift/component/ListJadwalShift.jsx @@ -10,68 +10,42 @@ import { import { NotifAlert, NotifConfirmDialog } from '../../../components/Global/ToastNotif'; import { useNavigate } from 'react-router-dom'; import TableList from '../../../components/Global/TableList'; - -// --- DUMMY DATA (Initial State) --- // -const initialDummyData = [ - { - id: 1, - nama_shift: 'Shift Pagi', - jam_masuk: '07:00', - jam_pulang: '15:00', - username: 'd.sanjaya', - nama_employee: 'Dede Sanjaya', - whatsapp: '081234567890' - }, - { - id: 2, - nama_shift: 'Shift Siang', - jam_masuk: '15:00', - jam_pulang: '23:00', - username: 'a.wijaya', - nama_employee: 'Andi Wijaya', - whatsapp: '081234567891' - }, - { - id: 3, - nama_shift: 'Shift Malam', - jam_masuk: '23:00', - jam_pulang: '07:00', - username: 'b.cahya', - nama_employee: 'Budi Cahya', - whatsapp: '081234567892' - }, -]; +import { getAllJadwalShift, deleteJadwalShift } from '../../../api/jadwal-shift'; const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [ { - title: 'Nama Karyawan', - dataIndex: 'nama_employee', - key: 'nama_employee', - }, - { - title: 'Username', - dataIndex: 'username', - key: 'username', + title: 'Tanggal Jadwal', + dataIndex: 'schedule_date', + key: 'schedule_date', + render: (date) => date ? new Date(date).toLocaleDateString('id-ID') : '-', }, { title: 'Nama Shift', - dataIndex: 'nama_shift', - key: 'nama_shift', + dataIndex: 'shift_name', + key: 'shift_name', + render: (text) => text || '-', }, { title: 'Jam Masuk', - dataIndex: 'jam_masuk', - key: 'jam_masuk', + dataIndex: 'start_time', + key: 'start_time', + render: (time) => time || '-', }, { title: 'Jam Pulang', - dataIndex: 'jam_pulang', - key: 'jam_pulang', + dataIndex: 'end_time', + key: 'end_time', + render: (time) => time || '-', }, { - title: 'Whatsapp', - dataIndex: 'whatsapp', - key: 'whatsapp', + title: 'Status', + dataIndex: 'is_active', + key: 'is_active', + render: (isActive) => ( + + {isActive ? 'Aktif' : 'Tidak Aktif'} + + ), }, { title: 'Aksi', @@ -101,39 +75,42 @@ const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [ ]; const ListJadwalShift = memo(function ListJadwalShift(props) { - const [dataSource, setDataSource] = useState(initialDummyData); const [trigerFilter, setTrigerFilter] = useState(false); const defaultFilter = { criteria: '' }; const [formDataFilter, setFormDataFilter] = useState(defaultFilter); const [searchValue, setSearchValue] = useState(''); const navigate = useNavigate(); - // --- DUMMY API --- // - const getDummyData = (queryParams) => { - return new Promise((resolve) => { - const { criteria } = queryParams; - let data = dataSource; - if (criteria) { - data = dataSource.filter(item => - item.nama_employee.toLowerCase().includes(criteria.toLowerCase()) || - item.username.toLowerCase().includes(criteria.toLowerCase()) - ); - } - setTimeout(() => { - resolve({ - status: 200, - data: { - data: data, - paging: { - page: 1, - limit: 10, - total: data.length, - page_total: 1 - } + const getData = async (queryParams) => { + try { + const params = new URLSearchParams({ + page: queryParams.page || 1, + limit: queryParams.limit || 10, + criteria: queryParams.criteria || '' + }); + + const response = await getAllJadwalShift(params); + return response; + } catch (error) { + console.error('Error fetching jadwal shift:', error); + NotifAlert({ + icon: 'error', + title: 'Error', + message: 'Gagal mengambil data jadwal shift.', + }); + return { + status: 500, + data: { + data: [], + paging: { + page: 1, + limit: 10, + total: 0, + page_total: 0 } - }); - }, 100); - }); + } + }; + } }; useEffect(() => { @@ -146,7 +123,7 @@ const ListJadwalShift = memo(function ListJadwalShift(props) { } else { navigate('/signin'); } - }, [props.actionMode, dataSource]); // Added dataSource to dependency array + }, [props.actionMode]); const doFilter = () => { setTrigerFilter((prev) => !prev); @@ -179,23 +156,41 @@ const ListJadwalShift = memo(function ListJadwalShift(props) { }; const showDeleteDialog = (param) => { + const dateStr = param.schedule_date ? new Date(param.schedule_date).toLocaleDateString('id-ID') : 'tanggal tidak diketahui'; NotifConfirmDialog({ icon: 'question', title: 'Konfirmasi Hapus', - message: `Jadwal shift untuk "${param.nama_employee}" akan dihapus?`, - onConfirm: () => handleDelete(param.id), + message: `Jadwal shift tanggal ${dateStr} akan dihapus?`, + onConfirm: () => handleDelete(param.schedule_id), onCancel: () => props.setSelectedData(null), }); }; - const handleDelete = (id) => { - setDataSource(prevData => prevData.filter(item => item.id !== id)); - NotifAlert({ - icon: 'success', - title: 'Berhasil', - message: 'Data Jadwal Shift berhasil dihapus.', - }); - doFilter(); // Trigger a re-fetch from the new state + const handleDelete = async (id) => { + try { + const response = await deleteJadwalShift(id); + if (response.statusCode === 200) { + NotifAlert({ + icon: 'success', + title: 'Berhasil', + message: 'Data Jadwal Shift berhasil dihapus.', + }); + doFilter(); + } else { + NotifAlert({ + icon: 'error', + title: 'Error', + message: response.message || 'Gagal menghapus data jadwal shift.', + }); + } + } catch (error) { + console.error('Error deleting jadwal shift:', error); + NotifAlert({ + icon: 'error', + title: 'Error', + message: 'Terjadi kesalahan saat menghapus data.', + }); + } }; return ( @@ -206,7 +201,7 @@ const ListJadwalShift = memo(function ListJadwalShift(props) { { const value = e.target.value; @@ -263,11 +258,11 @@ const ListJadwalShift = memo(function ListJadwalShift(props) { { const [actionMode, setActionMode] = useState('list'); const [selectedData, setSelectedData] = useState(null); - const [shiftData, setShiftData] = useState([]); - const [loading, setLoading] = useState(false); + const [readOnly, setReadOnly] = useState(false); + const [showModal, setShowModal] = useState(false); - const fetchData = async () => { - setLoading(true); - try { - const localData = localStorage.getItem('shiftData'); - if (localData) { - setShiftData(JSON.parse(localData)); - } else { - const response = await getAllShift(); - if (response.data) { - setShiftData(response.data.data); - localStorage.setItem('shiftData', JSON.stringify(response.data.data)); - } - } - } catch (error) { - console.error("Error fetching shift data:", error); + const setMode = (param) => { + setActionMode(param); + switch (param) { + case 'add': + setReadOnly(false); + setShowModal(true); + break; + + case 'edit': + setReadOnly(false); + setShowModal(true); + break; + + case 'preview': + setReadOnly(true); + setShowModal(true); + break; + + default: + setShowModal(false); + break; } - setLoading(false); }; useEffect(() => { - fetchData(); + const token = localStorage.getItem('token'); + if (token) { + setBreadcrumbItems([ + { title: • Master }, + { title: Shift } + ]); + } else { + navigate('/signin'); + } }, []); - const handleAddShift = (newShift) => { - const newData = { ...newShift, id: Date.now() }; // Simulate adding an ID - const updatedData = [newData, ...shiftData]; - setShiftData(updatedData); - localStorage.setItem('shiftData', JSON.stringify(updatedData)); - setActionMode('list'); - }; - - const handleUpdateShift = (updatedShift) => { - const updatedData = shiftData.map(shift => shift.id === updatedShift.id ? updatedShift : shift); - setShiftData(updatedData); - localStorage.setItem('shiftData', JSON.stringify(updatedData)); - setActionMode('list'); - }; - - const handleDeleteShift = (id) => { - const updatedData = shiftData.filter(shift => shift.id !== id); - setShiftData(updatedData); - localStorage.setItem('shiftData', JSON.stringify(updatedData)); - }; - - return ( - <> - {actionMode === 'list' && ( - - )} - {(actionMode === 'add' || actionMode === 'edit' || actionMode === 'preview') && ( - - )} - > + + + + ); -}; +}); export default IndexShift; diff --git a/src/pages/master/shift/component/DetailShift.jsx b/src/pages/master/shift/component/DetailShift.jsx index d0b354b..d8bd410 100644 --- a/src/pages/master/shift/component/DetailShift.jsx +++ b/src/pages/master/shift/component/DetailShift.jsx @@ -7,13 +7,13 @@ const { Text } = Typography; const DetailShift = (props) => { const [confirmLoading, setConfirmLoading] = useState(false); - const readOnly = props.actionMode === 'preview'; const defaultData = { - id: '', - nama_shift: '', - jam_shift: '', - status: true, // default to active + shift_id: '', + shift_name: '', + start_time: '', + end_time: '', + is_active: true, }; const [FormData, setFormData] = useState(defaultData); @@ -26,68 +26,228 @@ const DetailShift = (props) => { const handleSave = async () => { setConfirmLoading(true); - if (!FormData.nama_shift) { - NotifOk({ icon: 'warning', title: 'Peringatan', message: 'Kolom Nama Shift Tidak Boleh Kosong' }); + // Validasi required fields + if (!FormData.shift_name || FormData.shift_name.trim() === '') { + NotifOk({ + icon: 'warning', + title: 'Peringatan', + message: 'Kolom Nama Shift Tidak Boleh Kosong', + }); setConfirmLoading(false); return; } - if (!FormData.jam_shift) { - NotifOk({ icon: 'warning', title: 'Peringatan', message: 'Kolom Jam Shift Tidak Boleh Kosong' }); + if (!FormData.start_time || FormData.start_time.trim() === '') { + NotifOk({ + icon: 'warning', + title: 'Peringatan', + message: 'Kolom Jam Mulai Tidak Boleh Kosong', + }); setConfirmLoading(false); return; } - const payload = { - nama_shift: FormData.nama_shift, - jam_shift: FormData.jam_shift, - status: FormData.status, - }; + if (!FormData.end_time || FormData.end_time.trim() === '') { + NotifOk({ + icon: 'warning', + title: 'Peringatan', + message: 'Kolom Jam Selesai Tidak Boleh Kosong', + }); + setConfirmLoading(false); + return; + } + + // Validate time format + const timePattern = /^([01]\d|2[0-3]):([0-5]\d)(:[0-5]\d)?$/; + if (!timePattern.test(FormData.start_time)) { + NotifOk({ + icon: 'warning', + title: 'Peringatan', + message: + 'Format Jam Mulai tidak valid. Gunakan format HH:mm atau HH:mm:ss (contoh: 08:00)', + }); + setConfirmLoading(false); + return; + } + + if (!timePattern.test(FormData.end_time)) { + NotifOk({ + icon: 'warning', + title: 'Peringatan', + message: + 'Format Jam Selesai tidak valid. Gunakan format HH:mm atau HH:mm:ss (contoh: 17:00)', + }); + setConfirmLoading(false); + return; + } try { - if (FormData.id) { - props.onUpdate(payload); + if (FormData.shift_id) { + // Update existing shift + const payload = { + shift_name: FormData.shift_name, + start_time: FormData.start_time, + end_time: FormData.end_time, + is_active: FormData.is_active, + }; + + const response = await updateShift(FormData.shift_id, payload); + console.log('updateShift response:', response); + + if (response.statusCode === 200) { + NotifOk({ + icon: 'success', + title: 'Berhasil', + message: `Data Shift "${FormData.shift_name}" berhasil diubah.`, + }); + props.setActionMode('list'); + } else { + NotifAlert({ + icon: 'error', + title: 'Gagal', + message: response.message || 'Gagal mengubah data Shift.', + }); + } } else { - props.onAdd(payload); + // Create new shift + const payload = { + shift_name: FormData.shift_name, + start_time: FormData.start_time, + end_time: FormData.end_time, + is_active: FormData.is_active, + }; + + const response = await createShift(payload); + console.log('createShift response:', response); + + if (response.statusCode === 200 || response.statusCode === 201) { + NotifOk({ + icon: 'success', + title: 'Berhasil', + message: `Data Shift "${FormData.shift_name}" berhasil ditambahkan.`, + }); + props.setActionMode('list'); + } else { + NotifAlert({ + icon: 'error', + title: 'Gagal', + message: response.message || 'Gagal menambahkan data Shift.', + }); + } } - NotifOk({ - icon: 'success', - title: 'Berhasil', - message: `Data Shift "${payload.nama_shift}" berhasil ${FormData.id ? 'diubah' : 'ditambahkan'}.`, - }); } catch (error) { console.error('Save Shift Error:', error); NotifAlert({ icon: 'error', title: 'Error', - message: error.message || 'Terjadi kesalahan pada server. Coba lagi nanti.', + message: error.message || 'Terjadi kesalahan saat menyimpan data.', }); } setConfirmLoading(false); }; - const handleInputChange = (e) => { - const { name, value } = e.target; - setFormData({ ...FormData, [name]: value }); + // Helper function to format time input + const formatTimeInput = (value) => { + if (!value) return value; + + // Remove any whitespace + value = value.trim(); + + // If user inputs single digit hour like "8:00", convert to "08:00" + const timeRegex = /^(\d{1,2}):(\d{2})(:\d{2})?$/; + const match = value.match(timeRegex); + + if (match) { + const hours = match[1].padStart(2, '0'); + const minutes = match[2]; + const seconds = match[3] || ''; + return `${hours}:${minutes}${seconds}`; + } + + return value; }; - const handleStatusToggle = (checked) => { - setFormData({ ...FormData, status: checked }); + const handleInputChange = (e) => { + const { name, value } = e.target; + + // Just set the value without formatting during typing + setFormData({ + ...FormData, + [name]: value, + }); + }; + + // Format time when user leaves the input field (onBlur) + const handleTimeBlur = (e) => { + const { name, value } = e.target; + + if (name === 'start_time' || name === 'end_time') { + const formattedValue = formatTimeInput(value); + setFormData({ + ...FormData, + [name]: formattedValue, + }); + } + }; + + const handleStatusToggle = (isChecked) => { + setFormData({ + ...FormData, + is_active: isChecked, + }); + }; + + // Helper function to extract time from ISO timestamp + const extractTime = (timeString) => { + if (!timeString) return ''; + + // If it's ISO timestamp like "1970-01-01T08:00:00.000Z" + if (timeString.includes('T')) { + const date = new Date(timeString); + const hours = String(date.getUTCHours()).padStart(2, '0'); + const minutes = String(date.getUTCMinutes()).padStart(2, '0'); + return `${hours}:${minutes}`; + } + + // If it's already in HH:mm:ss format, remove seconds + if (timeString.includes(':')) { + const parts = timeString.split(':'); + return `${parts[0]}:${parts[1]}`; + } + + return timeString; }; useEffect(() => { - if (props.selectedData) { - setFormData(props.selectedData); - } else { - setFormData(defaultData); + const token = localStorage.getItem('token'); + if (token) { + if (props.selectedData != null) { + // Only set fields that are in defaultData + const filteredData = { + shift_id: props.selectedData.shift_id || '', + shift_name: props.selectedData.shift_name || '', + start_time: extractTime(props.selectedData.start_time) || '', + end_time: extractTime(props.selectedData.end_time) || '', + is_active: props.selectedData.is_active ?? true, + }; + setFormData(filteredData); + } else { + setFormData(defaultData); + } } - }, [props.actionMode, props.selectedData]); + }, [props.showModal]); return ( @@ -100,7 +260,6 @@ const DetailShift = (props) => { defaultColor: '#23A55A', defaultBorderColor: '#23A55A', defaultHoverColor: '#23A55A', - defaultHoverBorderColor: '#23A55A', }, }, }} @@ -123,7 +282,7 @@ const DetailShift = (props) => { }, }} > - {!readOnly && ( + {!props.readOnly && ( Simpan @@ -134,7 +293,8 @@ const DetailShift = (props) => { > {FormData && ( - + {/* Status Toggle */} + Status @@ -147,42 +307,66 @@ const DetailShift = (props) => { > - {FormData.status === true ? 'Active' : 'Inactive'} + {FormData.is_active === true ? 'Active' : 'Inactive'} - Nama Shift * - Jam Shift + Jam Mulai * + + Contoh: 08:00 atau 08:00:00 + + + + Jam Selesai + * + + + Contoh: 17:00 atau 17:00:00 + )} @@ -190,4 +374,4 @@ const DetailShift = (props) => { ); }; -export default DetailShift; \ No newline at end of file +export default DetailShift; diff --git a/src/pages/master/shift/component/ListShift.jsx b/src/pages/master/shift/component/ListShift.jsx index 303c76f..1a2e07e 100644 --- a/src/pages/master/shift/component/ListShift.jsx +++ b/src/pages/master/shift/component/ListShift.jsx @@ -1,183 +1,305 @@ import React, { memo, useState, useEffect } from 'react'; -import { Button, Col, Row, Input, ConfigProvider, Card, Tag, Table, Space } from 'antd'; +import { Space, Tag, ConfigProvider, Button, Row, Col, Card, Input } from 'antd'; import { PlusOutlined, EditOutlined, DeleteOutlined, - SearchOutlined, EyeOutlined, - SyncOutlined, + SearchOutlined, } from '@ant-design/icons'; -import { NotifConfirmDialog } from '../../../../components/Global/ToastNotif'; +import { NotifAlert, NotifConfirmDialog } from '../../../../components/Global/ToastNotif'; import { useNavigate } from 'react-router-dom'; +import TableList from '../../../../components/Global/TableList'; +import { getAllShift, deleteShift } from '../../../../api/master-shift'; -const ListShift = (props) => { +// Helper function to extract time from ISO timestamp +const extractTime = (timeString) => { + if (!timeString) return '-'; + + // If it's ISO timestamp like "1970-01-01T08:00:00.000Z" + if (timeString.includes('T')) { + const date = new Date(timeString); + const hours = String(date.getUTCHours()).padStart(2, '0'); + const minutes = String(date.getUTCMinutes()).padStart(2, '0'); + return `${hours}:${minutes}`; + } + + // If it's already in HH:mm or HH:mm:ss format + if (timeString.includes(':')) { + const parts = timeString.split(':'); + return `${parts[0]}:${parts[1]}`; // Return HH:mm only + } + + return timeString; +}; + +const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [ + { + title: 'No', + key: 'no', + width: '5%', + align: 'center', + render: (_, __, index) => index + 1, + }, + { + title: 'Nama Shift', + dataIndex: 'shift_name', + key: 'shift_name', + width: '20%', + }, + { + title: 'Jam Mulai', + dataIndex: 'start_time', + key: 'start_time', + width: '15%', + render: (time) => extractTime(time), + }, + { + title: 'Jam Selesai', + dataIndex: 'end_time', + key: 'end_time', + width: '15%', + render: (time) => extractTime(time), + }, + { + title: 'Status', + dataIndex: 'is_active', + key: 'is_active', + width: '10%', + align: 'center', + render: (_, { is_active }) => { + const color = is_active ? 'green' : 'red'; + const text = is_active ? 'Active' : 'Inactive'; + return ( + + {text} + + ); + }, + }, + { + title: 'Aksi', + key: 'aksi', + align: 'center', + width: '20%', + render: (_, record) => ( + + } + onClick={() => showPreviewModal(record)} + style={{ + color: '#1890ff', + borderColor: '#1890ff', + }} + /> + } + onClick={() => showEditModal(record)} + style={{ + color: '#faad14', + borderColor: '#faad14', + }} + /> + } + onClick={() => showDeleteDialog(record)} + style={{ + borderColor: '#ff4d4f', + }} + /> + + ), + }, +]; + +const ListShift = memo(function ListShift(props) { + const [trigerFilter, setTrigerFilter] = useState(false); + + const defaultFilter = { + criteria: '', + }; + const [formDataFilter, setFormDataFilter] = useState(defaultFilter); const [searchValue, setSearchValue] = useState(''); + const navigate = useNavigate(); useEffect(() => { const token = localStorage.getItem('token'); - if (!token) { + if (token) { + if (props.actionMode == 'list') { + doFilter(); + } + } else { navigate('/signin'); } - }, [navigate]); + }, [props.actionMode]); - const handleSearch = (value) => { - // This will be handled by the parent component if server-side search is needed - console.log('Search value:', value); + const doFilter = () => { + setTrigerFilter((prev) => !prev); + }; + + const handleSearch = () => { + setFormDataFilter((prev) => ({ ...prev, criteria: searchValue })); + doFilter(); }; const handleSearchClear = () => { setSearchValue(''); - // This will be handled by the parent component if server-side search is needed + setFormDataFilter((prev) => ({ ...prev, criteria: '' })); + doFilter(); }; - const showPreviewModal = (record) => { - props.setSelectedData(record); + const showPreviewModal = (param) => { + props.setSelectedData(param); props.setActionMode('preview'); }; - const showEditModal = (record) => { - props.setSelectedData(record); + const showEditModal = (param = null) => { + props.setSelectedData(param); props.setActionMode('edit'); }; - const showAddModal = () => { - props.setSelectedData(null); + const showAddModal = (param = null) => { + props.setSelectedData(param); props.setActionMode('add'); }; - const showDeleteDialog = (record) => { + const showDeleteDialog = (param) => { NotifConfirmDialog({ icon: 'question', title: 'Konfirmasi', - message: `Apakah anda yakin ingin menghapus data shift "${record.nama_shift}"?`, - onConfirm: () => props.onDelete(record.id), + message: `Apakah anda yakin hapus data "${param.shift_name}" ?`, + onConfirm: () => handleDelete(param), + onCancel: () => props.setSelectedData(null), }); }; - const columns = [ - { - title: 'No', - key: 'no', - width: '5%', - align: 'center', - render: (_, __, index) => index + 1, - }, - { - title: 'Nama Shift', - dataIndex: 'nama_shift', - key: 'nama_shift', - }, - { - title: 'Jam Shift', - dataIndex: 'jam_shift', - key: 'jam_shift', - }, - { - title: 'Status', - dataIndex: 'status', - key: 'status', - align: 'center', - render: (status) => ( - - {status ? 'Active' : 'Inactive'} - - ), - }, - { - title: 'Aksi', - key: 'action', - align: 'center', - width: '15%', - render: (_, record) => ( - - } - onClick={() => showPreviewModal(record)} - style={{ - color: '#1890ff', - borderColor: '#1890ff', - }} - /> - } - onClick={() => showEditModal(record)} - style={{ - color: '#faad14', - borderColor: '#faad14', - }} - /> - } - onClick={() => showDeleteDialog(record)} - style={{ - borderColor: '#ff4d4f', - }} - /> - - ), - }, - ]; + const handleDelete = async (param) => { + try { + const response = await deleteShift(param.shift_id); + console.log('deleteShift response:', response); - const filteredData = props.shiftData.filter(item => - item.nama_shift.toLowerCase().includes(searchValue.toLowerCase()) - ); + if (response.statusCode === 200) { + NotifAlert({ + icon: 'success', + title: 'Berhasil', + message: `Data Shift "${param.shift_name}" berhasil dihapus.`, + }); + // Refresh table + doFilter(); + } else { + NotifAlert({ + icon: 'error', + title: 'Gagal', + message: response.message || 'Gagal menghapus data Shift.', + }); + } + } catch (error) { + console.error('Delete Shift Error:', error); + NotifAlert({ + icon: 'error', + title: 'Error', + message: error.message || 'Terjadi kesalahan saat menghapus data.', + }); + } + }; + + // Function untuk dipanggil dari DetailShift setelah create/update + const refreshData = () => { + doFilter(); + }; + + // Pass refresh function to props + if (props.setRefreshData) { + props.setRefreshData(refreshData); + } return ( - - - - setSearchValue(e.target.value)} - onSearch={handleSearch} - allowClear={{ - clearIcon: x, - }} - enterButton={} style={{ backgroundColor: '#23A55A', borderColor: '#23A55A' }}>Cari} - size="large" - /> - - - - } - onClick={showAddModal} - size="large" - > - Tambah Shift - - - - - - - - - - + + + + + + + { + const value = e.target.value; + setSearchValue(value); + // Auto search when clearing by backspace/delete + if (value === '') { + handleSearchClear(); + } + }} + onSearch={handleSearch} + allowClear + onClear={handleSearchClear} + enterButton={ + } + style={{ + backgroundColor: '#23A55A', + borderColor: '#23A55A', + }} + > + Search + + } + size="large" + /> + + + + + } + onClick={() => showAddModal()} + size="large" + > + Tambah Data + + + + + + + + + + + + ); -}; +}); -export default memo(ListShift); \ No newline at end of file +export default ListShift; \ No newline at end of file