From 973713286fe59888ae884a53a563e577746db34c Mon Sep 17 00:00:00 2001 From: Iqbal Rizqi Kurniawan Date: Mon, 13 Oct 2025 23:13:40 +0700 Subject: [PATCH] feat: update shift management with jadwal shift integration and improved error handling --- src/App.jsx | 2 - src/layout/LayoutMenu.jsx | 15 +- src/pages/jadwalShift/IndexJadwalShift.jsx | 36 +++- .../component/DetailJadwalShift.jsx | 19 +- .../jadwalShift/component/ListJadwalShift.jsx | 196 ++++++++++++------ 5 files changed, 192 insertions(+), 76 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 6bf096f..106c0a3 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -31,7 +31,6 @@ import IndexRole from './pages/role/IndexRole'; import IndexUser from './pages/user/IndexUser'; // Shift Management -import IndexSchedule from './pages/shiftManagement/schedule/IndexSchedule'; import IndexMember from './pages/shiftManagement/member/IndexMember'; import SvgTest from './pages/home/SvgTest'; @@ -87,7 +86,6 @@ const App = () => { }> - } /> } /> diff --git a/src/layout/LayoutMenu.jsx b/src/layout/LayoutMenu.jsx index 97f044f..2dda2c5 100644 --- a/src/layout/LayoutMenu.jsx +++ b/src/layout/LayoutMenu.jsx @@ -130,16 +130,20 @@ const allItems = [ ), }, + { + key: 'jadwal-shift', + icon: , + label: ( + + Jadwal Shift + + ), + }, { key: 'shift-management', icon: , label: 'Manajemen Shift', children: [ - { - key: 'shift-schedule', - icon: , - label: Jadwal Shift, - }, { key: 'shift-member', icon: , @@ -165,6 +169,7 @@ const LayoutMenu = () => { if (pathname === '/role') return 'role'; if (pathname === '/notification') return 'notification'; if (pathname === '/event-alarm') return 'event-alarm'; + if (pathname === '/jadwal-shift') return 'jadwal-shift'; // Handle master routes if (pathname.startsWith('/master/')) { diff --git a/src/pages/jadwalShift/IndexJadwalShift.jsx b/src/pages/jadwalShift/IndexJadwalShift.jsx index 36e2fc8..0a9f7ea 100644 --- a/src/pages/jadwalShift/IndexJadwalShift.jsx +++ b/src/pages/jadwalShift/IndexJadwalShift.jsx @@ -1,10 +1,39 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import ListJadwalShift from './component/ListJadwalShift'; import DetailJadwalShift from './component/DetailJadwalShift'; +import { getAllJadwalShift, deleteJadwalShift } from '../../api/jadwal-shift'; const IndexJadwalShift = () => { const [actionMode, setActionMode] = useState('list'); const [selectedData, setSelectedData] = useState(null); + const [jadwalShiftData, setJadwalShiftData] = useState([]); + const [loading, setLoading] = useState(false); + + const fetchData = async () => { + setLoading(true); + try { + const response = await getAllJadwalShift(new URLSearchParams()); + if (response.data) { + setJadwalShiftData(response.data.data); + } + } catch (error) { + console.error("Error fetching jadwal shift data:", error); + } + setLoading(false); + }; + + useEffect(() => { + fetchData(); + }, []); + + const handleDelete = async (id) => { + try { + await deleteJadwalShift(id); + fetchData(); + } catch (error) { + console.error("Error deleting jadwal shift:", error); + } + }; return ( <> @@ -12,6 +41,9 @@ const IndexJadwalShift = () => { )} {(actionMode === 'add' || actionMode === 'edit') && ( @@ -19,6 +51,8 @@ const IndexJadwalShift = () => { actionMode={actionMode} selectedData={selectedData} setActionMode={setActionMode} + setSelectedData={setSelectedData} + fetchData={fetchData} /> )} diff --git a/src/pages/jadwalShift/component/DetailJadwalShift.jsx b/src/pages/jadwalShift/component/DetailJadwalShift.jsx index 1e11da8..697e6c5 100644 --- a/src/pages/jadwalShift/component/DetailJadwalShift.jsx +++ b/src/pages/jadwalShift/component/DetailJadwalShift.jsx @@ -22,13 +22,23 @@ const DetailJadwalShift = (props) => { const [FormData, setFormData] = useState(defaultData); const fetchShifts = async () => { - const response = await getAllShift(new URLSearchParams()); - setShifts(response.data.data); + try { + const response = await getAllShift(new URLSearchParams()); + setShifts(response?.data?.data || []); + } catch (error) { + console.error("Failed to fetch shifts:", error); + setShifts([]); + } }; const fetchUsers = async () => { - const response = await getAllUser(new URLSearchParams("limit=1000")); // Fetch all users - setUsers(response.data.data); + try { + const response = await getAllUser(new URLSearchParams("limit=1000")); // Fetch all users + setUsers(response?.data?.data || []); + } catch (error) { + console.error("Failed to fetch users:", error); + setUsers([]); + } }; useEffect(() => { @@ -82,6 +92,7 @@ const DetailJadwalShift = (props) => { }); props.setActionMode('list'); + props.fetchData(); } else { NotifAlert({ icon: 'error', diff --git a/src/pages/jadwalShift/component/ListJadwalShift.jsx b/src/pages/jadwalShift/component/ListJadwalShift.jsx index 641a33c..3bfdacd 100644 --- a/src/pages/jadwalShift/component/ListJadwalShift.jsx +++ b/src/pages/jadwalShift/component/ListJadwalShift.jsx @@ -1,60 +1,66 @@ -import { useEffect, useState } from 'react'; -import { Button, Table, Space, Popconfirm } from 'antd'; -import { getAllJadwalShift, deleteJadwalShift } from '../../../api/jadwal-shift'; -import { NotifAlert, NotifOk } from '../../../components/Global/ToastNotif'; +import React, { memo, useState, useEffect } from 'react'; +import { Button, Col, Row, Input, ConfigProvider, Card, Table, Space } from 'antd'; +import { + PlusOutlined, + EditOutlined, + DeleteOutlined, + SearchOutlined, + EyeOutlined, +} from '@ant-design/icons'; +import { NotifConfirmDialog } from '../../../components/Global/ToastNotif'; +import { useNavigate } from 'react-router-dom'; const ListJadwalShift = (props) => { - const [data, setData] = useState([]); - const [loading, setLoading] = useState(false); + const [searchValue, setSearchValue] = useState(''); + const navigate = useNavigate(); - const fetchData = async () => { - setLoading(true); - try { - const response = await getAllJadwalShift(new URLSearchParams()); - if (response.data && response.data.data) { - setData(response.data.data); - } else { - setData([]); - } - } catch (error) { - console.error("Failed to fetch shift schedule data:", error); - setData([]); // Set data to empty array on error - } finally { - setLoading(false); + useEffect(() => { + const token = localStorage.getItem('token'); + if (!token) { + navigate('/signin'); } + }, [navigate]); + + const handleSearch = (value) => { + console.log('Search value:', value); }; - const handleAdd = () => { - props.setSelectedData(null); - props.setActionMode('add'); + const handleSearchClear = () => { + setSearchValue(''); }; - const handleEdit = (record) => { + const showPreviewModal = (record) => { + props.setSelectedData(record); + props.setActionMode('preview'); + }; + + const showEditModal = (record) => { props.setSelectedData(record); props.setActionMode('edit'); }; - const handleDelete = async (id) => { - try { - const response = await deleteJadwalShift(id); - if (response.statusCode === 200) { - NotifOk({ icon: 'success', title: 'Berhasil', message: 'Data jadwal shift berhasil dihapus' }); - fetchData(); - } else { - NotifAlert({ icon: 'error', title: 'Gagal', message: response.message }); - } - } catch (error) { - NotifAlert({ icon: 'error', title: 'Error', message: error.message }); - } + const showAddModal = () => { + props.setSelectedData(null); + props.setActionMode('add'); }; - useEffect(() => { - if(props.actionMode === 'list') { - fetchData(); - } - }, [props.actionMode]); + const showDeleteDialog = (record) => { + NotifConfirmDialog({ + icon: 'question', + title: 'Konfirmasi', + message: `Apakah anda yakin ingin menghapus data jadwal shift untuk "${record.nama_employee}"?`, + onConfirm: () => props.onDelete(record.id), + }); + }; const columns = [ + { + title: 'No', + key: 'no', + width: '5%', + align: 'center', + render: (_, __, index) => index + 1, + }, { title: 'Nama Shift', dataIndex: 'nama_shift', @@ -83,35 +89,97 @@ const ListJadwalShift = (props) => { { title: 'Aksi', key: 'action', - render: (text, record) => ( - - - handleDelete(record.id)} - okText="Yes" - cancelText="No" - > - - + align: 'center', + width: '15%', + render: (_, record) => ( + + - - + + + + setSearchValue(e.target.value)} + onSearch={handleSearch} + allowClear={{ + clearIcon: x, + }} + enterButton={} + size="large" + /> + + + + + + + + + +
+ + + ); };