From bc463288329e224c638fc85a0bda14abf9291725 Mon Sep 17 00:00:00 2001 From: vinix Date: Wed, 8 Oct 2025 14:45:41 +0700 Subject: [PATCH] feat: implement user management API functions and create ListUser component for user listing --- src/api/user.jsx | 103 ++++++++ src/pages/user/IndexUser.jsx | 50 +++- src/pages/user/component/ListUser.jsx | 349 ++++++++++++++++++++++++++ 3 files changed, 497 insertions(+), 5 deletions(-) create mode 100644 src/api/user.jsx create mode 100644 src/pages/user/component/ListUser.jsx diff --git a/src/api/user.jsx b/src/api/user.jsx new file mode 100644 index 0000000..5812dd7 --- /dev/null +++ b/src/api/user.jsx @@ -0,0 +1,103 @@ +import { SendRequest } from '../components/Global/ApiRequest'; + +const getAllUser = async (queryParams) => { + const response = await SendRequest({ + method: 'get', + prefix: `user?${queryParams.toString()}`, + }); + + // Parse query params to get page and limit + const params = Object.fromEntries(queryParams); + const currentPage = parseInt(params.page) || 1; + const currentLimit = parseInt(params.limit) || 10; + + // Backend returns all data, so we need to do client-side pagination + const allData = response.data || []; + const totalData = allData.length; + + // Calculate start and end index for current page + const startIndex = (currentPage - 1) * currentLimit; + const endIndex = startIndex + currentLimit; + + // Slice data for current page + const paginatedData = allData.slice(startIndex, endIndex); + + // Transform response to match TableList expected structure + return { + status: response.statusCode || 200, + data: { + data: paginatedData, + paging: { + page: currentPage, + limit: currentLimit, + total: totalData, + page_total: Math.ceil(totalData / currentLimit) + }, + total: totalData + } + }; +}; + +const getUserById = async (id) => { + const response = await SendRequest({ + method: 'get', + prefix: `user/${id}`, + }); + return response.data; +}; + +const createUser = async (queryParams) => { + const response = await SendRequest({ + method: 'post', + prefix: `user`, + params: queryParams, + }); + // Return full response with statusCode + return { + statusCode: response.statusCode || 200, + data: response.data, + message: response.message + }; +}; + +const updateUser = async (user_id, queryParams) => { + const response = await SendRequest({ + method: 'put', + prefix: `user/${user_id}`, + params: queryParams, + }); + // Return full response with statusCode + return { + statusCode: response.statusCode || 200, + data: response.data, + message: response.message + }; +}; + +const deleteUser = async (queryParams) => { + const response = await SendRequest({ + method: 'delete', + prefix: `user/${queryParams}`, + }); + // Return full response with statusCode + return { + statusCode: response.statusCode || 200, + data: response.data, + message: response.message + }; +}; + +const approveUser = async (user_id) => { + const response = await SendRequest({ + method: 'put', + prefix: `user/${user_id}/approve`, + }); + // Return full response with statusCode + return { + statusCode: response.statusCode || 200, + data: response.data, + message: response.message + }; +}; + +export { getAllUser, getUserById, createUser, updateUser, deleteUser, approveUser }; \ No newline at end of file diff --git a/src/pages/user/IndexUser.jsx b/src/pages/user/IndexUser.jsx index 373aa47..1fb19d6 100644 --- a/src/pages/user/IndexUser.jsx +++ b/src/pages/user/IndexUser.jsx @@ -1,5 +1,6 @@ -import React, { memo, useEffect } from 'react'; +import React, { memo, useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; +import ListUser from './component/ListUser'; import { useBreadcrumb } from '../../layout/LayoutBreadcrumb'; import { Typography } from 'antd'; @@ -9,11 +10,44 @@ const IndexUser = memo(function IndexUser() { const navigate = useNavigate(); const { setBreadcrumbItems } = useBreadcrumb(); + const [actionMode, setActionMode] = useState('list'); + const [selectedData, setSelectedData] = useState(null); + const [readOnly, setReadOnly] = useState(false); + const [showModal, setShowmodal] = useState(false); + + const setMode = (param) => { + setShowmodal(true); + switch (param) { + case 'add': + setReadOnly(false); + break; + + case 'edit': + setReadOnly(false); + break; + + case 'preview': + setReadOnly(true); + break; + + default: + setShowmodal(false); + break; + } + setActionMode(param); + }; + useEffect(() => { const token = localStorage.getItem('token'); if (token) { setBreadcrumbItems([ - { title: • User } + { + title: ( + + • User + + ), + }, ]); } else { navigate('/signin'); @@ -21,9 +55,15 @@ const IndexUser = memo(function IndexUser() { }, []); return ( -
-

User Page

-
+ + + ); }); diff --git a/src/pages/user/component/ListUser.jsx b/src/pages/user/component/ListUser.jsx new file mode 100644 index 0000000..a716544 --- /dev/null +++ b/src/pages/user/component/ListUser.jsx @@ -0,0 +1,349 @@ +import React, { memo, useState, useEffect } from 'react'; +import { Space, Tag, ConfigProvider, Button, Row, Col, Card, Input } from 'antd'; +import { + PlusOutlined, + EditOutlined, + DeleteOutlined, + EyeOutlined, + SearchOutlined, + CheckOutlined, + CloseOutlined, +} from '@ant-design/icons'; +import { NotifAlert, NotifOk, NotifConfirmDialog } from '../../../components/Global/ToastNotif'; +import { useNavigate } from 'react-router-dom'; +import { deleteUser, getAllUser } from '../../../api/user'; +import TableList from '../../../components/Global/TableList'; + +const columns = (showPreviewModal, showEditModal, showDeleteDialog, showApproveDialog) => [ + { + title: 'ID', + dataIndex: 'user_id', + key: 'user_id', + width: '5%', + hidden: 'true', + }, + { + title: 'Username', + dataIndex: 'username', + key: 'username', + width: '12%', + }, + { + title: 'Nama Lengkap', + dataIndex: 'fullname', + key: 'fullname', + width: '15%', + }, + { + title: 'Nomor WA', + dataIndex: 'whatsapp_number', + key: 'whatsapp_number', + width: '12%', + }, + { + title: 'Level', + dataIndex: 'level', + key: 'level', + width: '8%', + align: 'center', + }, + { + title: 'Nama Role', + dataIndex: 'role_name', + key: 'role_name', + width: '12%', + render: (_, { role_name }) => ( + <> + {role_name === 'administrator' && ( + + Administrator + + )} + {role_name === 'operator' && ( + + Operator + + )} + {role_name === 'engineer' && ( + + Engineer + + )} + {role_name === 'guest' && ( + + Guest + + )} + + ), + }, + { + title: 'Status', + dataIndex: 'status', + key: 'status', + width: '10%', + align: 'center', + render: (_, { status }) => ( + <> + {status === 'active' && ( + + Active + + )} + {status === 'pending' && ( + + Pending + + )} + {status === 'inactive' && ( + + Inactive + + )} + {status === 'rejected' && ( + + Rejected + + )} + + ), + }, + { + title: 'Aksi', + key: 'aksi', + align: 'center', + width: '15%', + render: (_, record) => ( + + + } + size="large" + /> + + + + + + + + + + + + + + + + + ); +}); + +export default ListUser;