diff --git a/src/pages/master/brandDevice/IndexBrandDevice.jsx b/src/pages/master/brandDevice/IndexBrandDevice.jsx
index aa60fc3..a8aac49 100644
--- a/src/pages/master/brandDevice/IndexBrandDevice.jsx
+++ b/src/pages/master/brandDevice/IndexBrandDevice.jsx
@@ -1,13 +1,97 @@
-import React, { memo, useEffect } from 'react';
+
+import React, { memo, useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb';
-import { Typography } from 'antd';
+import { Form, Modal, Typography } from 'antd';
+import ListBrandDevice from './component/ListBrandDevice';
+import DetailBrandDevice from './component/DetailBrandDevice';
+
+import { NotifConfirmDialog, NotifAlert } from '../../../components/Global/ToastNotif';
const { Text } = Typography;
+// Mock Data
+const initialData = [
+ {
+ key: '1',
+ brandCode: 'HTC-01',
+ brandName: 'Brand A',
+ brandType: 'Type X',
+ deviceName: 'Device 1',
+ description: 'Description for Brand A',
+ },
+ {
+ key: '2',
+ brandCode: 'HTC-02',
+ brandName: 'Brand B',
+ brandType: 'Type Y',
+ deviceName: 'Device 2',
+ description: 'Description for Brand B',
+ },
+ {
+ key: '3',
+ brandCode: 'HTC-03',
+ brandName: 'Brand C',
+ brandType: 'Type Z',
+ deviceName: 'Device 3',
+ description: 'Description for Brand C',
+ },
+ // Add more data for pagination testing
+ ...Array.from({ length: 20 }, (_, i) => ({
+ key: `${i + 4}`,
+ brandCode: `HTC-${String(i + 4).padStart(2, '0')}`,
+ brandName: `Brand ${String.fromCharCode(68 + i)}`,
+ brandType: `Type ${String.fromCharCode(88 + i)}`,
+ deviceName: `Device ${i + 4}`,
+ description: `Description for Brand ${String.fromCharCode(68 + i)}`,
+ })),
+];
+
const IndexBrandDevice = memo(function IndexBrandDevice() {
const navigate = useNavigate();
const { setBreadcrumbItems } = useBreadcrumb();
+ const [form] = Form.useForm();
+
+ const [data, setData] = useState(initialData);
+ const [actionMode, setActionMode] = useState('list');
+ const [editingKey, setEditingKey] = useState('');
+ const [isModalVisible, setIsModalVisible] = useState(false);
+ const [readOnly, setReadOnly] = useState(false);
+
+ // Mock API function
+ const getAllBrandDevice = async (params) => {
+ const { page = 1, limit = 10, search = '' } = Object.fromEntries(params.entries());
+
+ let filteredData = data;
+ if (search) {
+ filteredData = data.filter(item =>
+ item.brandName.toLowerCase().includes(search.toLowerCase()) ||
+ item.brandType.toLowerCase().includes(search.toLowerCase()) ||
+ item.deviceName.toLowerCase().includes(search.toLowerCase())
+ );
+ }
+
+ const start = (page - 1) * limit;
+ const end = start + limit;
+ const paginatedData = filteredData.slice(start, end);
+
+ return new Promise(resolve => {
+ setTimeout(() => {
+ resolve({
+ status: 200,
+ data: {
+ data: paginatedData,
+ total: filteredData.length,
+ paging: {
+ page: parseInt(page),
+ limit: parseInt(limit),
+ total: filteredData.length,
+ },
+ },
+ });
+ }, 500);
+ });
+ };
useEffect(() => {
const token = localStorage.getItem('token');
@@ -19,12 +103,98 @@ const IndexBrandDevice = memo(function IndexBrandDevice() {
} else {
navigate('/signin');
}
- }, []);
+ }, [navigate, setBreadcrumbItems]);
+
+ useEffect(() => {
+ if (actionMode === 'add' || actionMode === 'edit' || actionMode === 'preview') {
+ setIsModalVisible(true);
+ setReadOnly(actionMode === 'preview');
+ } else {
+ setIsModalVisible(false);
+ }
+ }, [actionMode]);
+
+ const handleCancel = () => {
+ setActionMode('list');
+ setEditingKey('');
+ form.resetFields();
+ };
+
+ const handleOk = () => {
+ if (readOnly) {
+ handleCancel();
+ return;
+ }
+ form.validateFields()
+ .then((values) => {
+ let newData = [...data];
+ if (editingKey) {
+ // Editing existing data
+ const index = newData.findIndex((item) => editingKey === item.key);
+ if (index > -1) {
+ const item = newData[index];
+ newData.splice(index, 1, { ...item, ...values });
+ }
+ } else {
+ // Adding new data
+ const newKey = (Math.max(...data.map(item => parseInt(item.key))) + 1).toString();
+ newData = [{ key: newKey, ...values }, ...newData];
+ }
+ setData(newData);
+ handleCancel();
+ })
+ .catch((info) => {
+ console.log('Validate Failed:', info);
+ });
+ };
+
+ const handleEdit = (record) => {
+ form.setFieldsValue(record);
+ setEditingKey(record.key);
+ setActionMode('edit');
+ };
+
+ const handlePreview = (record) => {
+ form.setFieldsValue(record);
+ setEditingKey(record.key);
+ setActionMode('preview');
+ };
+
+ const handleDelete = (record) => {
+ NotifConfirmDialog({
+ icon: 'question',
+ title: 'Konfirmasi',
+ message: `Apakah anda yakin ingin menghapus brand "${record.brandName}"?`,
+ onConfirm: () => {
+ const newData = data.filter((item) => item.key !== record.key);
+ setData(newData);
+ NotifAlert({
+ icon: 'success',
+ title: 'Berhasil',
+ message: `Brand "${record.brandName}" berhasil dihapus.`,
+ });
+ },
+ });
+ };
return (
-
-
Brand Device Page
-
+
+
+
+
);
});
diff --git a/src/pages/master/brandDevice/component/DetailBrandDevice.jsx b/src/pages/master/brandDevice/component/DetailBrandDevice.jsx
new file mode 100644
index 0000000..f2a4a3a
--- /dev/null
+++ b/src/pages/master/brandDevice/component/DetailBrandDevice.jsx
@@ -0,0 +1,117 @@
+import React from 'react';
+import { Modal, Form, Input, ConfigProvider, Button, Typography } from 'antd';
+
+const { Text } = Typography;
+
+const DetailBrandDevice = ({ visible, onCancel, onOk, form, editingKey, readOnly }) => {
+ const modalTitle = readOnly ? 'Preview Brand' : (editingKey ? 'Edit Brand' : 'Tambah Brand');
+
+ return (
+
+
+
+
+
+ {!readOnly && (
+
+ )}
+
+ ,
+ ]}
+ destroyOnClose
+ >
+
+
+ );
+};
+
+export default DetailBrandDevice;
\ No newline at end of file
diff --git a/src/pages/master/brandDevice/component/ListBrandDevice.jsx b/src/pages/master/brandDevice/component/ListBrandDevice.jsx
new file mode 100644
index 0000000..b015d6c
--- /dev/null
+++ b/src/pages/master/brandDevice/component/ListBrandDevice.jsx
@@ -0,0 +1,137 @@
+import React, { useState } from 'react';
+import { Button, Col, Row, Space, Input, ConfigProvider, Card } from 'antd';
+import { PlusOutlined, EditOutlined, DeleteOutlined, SearchOutlined, EyeOutlined } from '@ant-design/icons';
+import TableList from '../../../../components/Global/TableList';
+
+const ListBrandDevice = ({
+ setActionMode,
+ handleEdit,
+ handleDelete,
+ handlePreview,
+ getAllBrandDevice // Mock API function
+}) => {
+ const [searchValue, setSearchValue] = useState('');
+ const [formDataFilter, setFormDataFilter] = useState({ search: '' });
+ const [trigerFilter, setTrigerFilter] = useState(false);
+
+ const columns = [
+ {
+ title: 'Kode Brand',
+ dataIndex: 'brandCode',
+ key: 'brandCode',
+ },
+ {
+ title: 'Brand Name',
+ dataIndex: 'brandName',
+ key: 'brandName',
+ },
+ {
+ title: 'Brand Type',
+ dataIndex: 'brandType',
+ key: 'brandType',
+ },
+ {
+ title: 'Device Name',
+ dataIndex: 'deviceName',
+ key: 'deviceName',
+ },
+ {
+ title: 'Description',
+ dataIndex: 'description',
+ key: 'description',
+ },
+ {
+ title: 'Aksi',
+ key: 'action',
+ render: (_, record) => (
+
+ } onClick={() => handlePreview(record)} />
+ } onClick={() => handleEdit(record)} />
+ } onClick={() => handleDelete(record)} />
+
+ ),
+ },
+ ];
+
+ const handleSearch = () => {
+ setFormDataFilter({ search: searchValue });
+ setTrigerFilter((prev) => !prev);
+ };
+
+ const handleSearchClear = () => {
+ setSearchValue('');
+ setFormDataFilter({ search: '' });
+ setTrigerFilter((prev) => !prev);
+ };
+
+ return (
+
+
+
+ {
+ const value = e.target.value;
+ setSearchValue(value);
+ if (value === '') {
+ handleSearchClear();
+ }
+ }}
+ onSearch={handleSearch}
+ allowClear={{
+ clearIcon: ✕,
+ }}
+ enterButton={
+ }
+ style={{
+ backgroundColor: '#23A55A',
+ borderColor: '#23A55A',
+ }}
+ >
+ Search
+
+ }
+ size="large"
+ />
+
+
+
+ }
+ onClick={() => setActionMode('add')}
+ size="large"
+ >
+ Tambah Data
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ListBrandDevice;
\ No newline at end of file
diff --git a/src/pages/master/tag/IndexTag.jsx b/src/pages/master/tag/IndexTag.jsx
index 17ef600..b763c27 100644
--- a/src/pages/master/tag/IndexTag.jsx
+++ b/src/pages/master/tag/IndexTag.jsx
@@ -1,5 +1,7 @@
-import React, { memo, useEffect } from 'react';
+import React, { memo, useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
+import ListTag from './component/ListTag';
+import DetailTag from './component/DetailTag';
import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb';
import { Typography } from 'antd';
@@ -9,6 +11,35 @@ const IndexTag = memo(function IndexTag() {
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) => {
+ 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;
+ }
+ };
+
useEffect(() => {
const token = localStorage.getItem('token');
if (token) {
@@ -22,9 +53,23 @@ const IndexTag = memo(function IndexTag() {
}, []);
return (
-
-
Tag Page
-
+
+
+
+
);
});
diff --git a/src/pages/master/tag/component/DetailTag.jsx b/src/pages/master/tag/component/DetailTag.jsx
new file mode 100644
index 0000000..754d49e
--- /dev/null
+++ b/src/pages/master/tag/component/DetailTag.jsx
@@ -0,0 +1,286 @@
+import { useEffect, useState } from 'react';
+import { Modal, Input, Divider, Typography, Button, ConfigProvider, Select } from 'antd';
+import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif';
+
+const { Text } = Typography;
+const { TextArea } = Input;
+
+const DetailTag = (props) => {
+ const [confirmLoading, setConfirmLoading] = useState(false);
+
+ const defaultData = {
+ tag_id: '',
+ tag_code: '',
+ tag_name: '',
+ tag_value: 'Off',
+ tag_type: 'alarm',
+ tag_description: '',
+ };
+
+ const [FormData, setFormData] = useState(defaultData);
+
+ const handleCancel = () => {
+ props.setSelectedData(null);
+ props.setActionMode('list');
+ };
+
+ const handleSave = async () => {
+ setConfirmLoading(true);
+
+ // Validasi required fields
+ if (!FormData.tag_code) {
+ NotifOk({
+ icon: 'warning',
+ title: 'Peringatan',
+ message: 'Kolom Tag Code Tidak Boleh Kosong',
+ });
+ setConfirmLoading(false);
+ return;
+ }
+
+ if (!FormData.tag_name) {
+ NotifOk({
+ icon: 'warning',
+ title: 'Peringatan',
+ message: 'Kolom Tag Name Tidak Boleh Kosong',
+ });
+ setConfirmLoading(false);
+ return;
+ }
+
+ if (!FormData.tag_value) {
+ NotifOk({
+ icon: 'warning',
+ title: 'Peringatan',
+ message: 'Kolom Tag Value Tidak Boleh Kosong',
+ });
+ setConfirmLoading(false);
+ return;
+ }
+
+ if (!FormData.tag_type) {
+ NotifOk({
+ icon: 'warning',
+ title: 'Peringatan',
+ message: 'Kolom Tag Type Tidak Boleh Kosong',
+ });
+ setConfirmLoading(false);
+ return;
+ }
+
+ const payload = {
+ tag_code: FormData.tag_code,
+ tag_name: FormData.tag_name,
+ tag_value: FormData.tag_value,
+ tag_type: FormData.tag_type,
+ tag_description: FormData.tag_description,
+ };
+
+ try {
+ // Simulate API call
+ await new Promise((resolve) => setTimeout(resolve, 500));
+
+ const response = {
+ statusCode: FormData.tag_id ? 200 : 201,
+ data: {
+ tag_name: FormData.tag_name,
+ },
+ };
+
+ console.log('Save Tag Response:', response);
+
+ // Check if response is successful
+ if (response && (response.statusCode === 200 || response.statusCode === 201)) {
+ NotifOk({
+ icon: 'success',
+ title: 'Berhasil',
+ message: `Data Tag "${response.data?.tag_name || FormData.tag_name}" berhasil ${
+ FormData.tag_id ? 'diubah' : 'ditambahkan'
+ }.`,
+ });
+
+ props.setActionMode('list');
+ } else {
+ NotifAlert({
+ icon: 'error',
+ title: 'Gagal',
+ message: response?.message || 'Terjadi kesalahan saat menyimpan data.',
+ });
+ }
+ } catch (error) {
+ console.error('Save Tag Error:', error);
+ NotifAlert({
+ icon: 'error',
+ title: 'Error',
+ message: error.message || 'Terjadi kesalahan pada server. Coba lagi nanti.',
+ });
+ }
+
+ setConfirmLoading(false);
+ };
+
+ const handleInputChange = (e) => {
+ const { name, value } = e.target;
+ setFormData({
+ ...FormData,
+ [name]: value,
+ });
+ };
+
+ const handleSelectChange = (name, value) => {
+ setFormData({
+ ...FormData,
+ [name]: value,
+ });
+ };
+
+ useEffect(() => {
+ const token = localStorage.getItem('token');
+ if (token) {
+ if (props.selectedData != null) {
+ setFormData(props.selectedData);
+ } else {
+ setFormData(defaultData);
+ }
+ } else {
+ // navigate('/signin'); // Uncomment if useNavigate is imported
+ }
+ }, [props.showModal]);
+
+ return (
+
+
+
+
+
+ {!props.readOnly && (
+
+ )}
+
+ >,
+ ]}
+ >
+ {FormData && (
+
+
+ Tag ID
+
+
+
+ Tag Code
+ *
+
+
+
+ Tag Name
+ *
+
+
+
+ Tag Value
+ *
+
+
+ Tag Type
+ *
+
+
+ Tag Description
+
+
+
+ )}
+
+ );
+};
+
+export default DetailTag;
diff --git a/src/pages/master/tag/component/ListTag.jsx b/src/pages/master/tag/component/ListTag.jsx
new file mode 100644
index 0000000..dc882e0
--- /dev/null
+++ b/src/pages/master/tag/component/ListTag.jsx
@@ -0,0 +1,486 @@
+import React, { memo, useState, useEffect } from 'react';
+import { Space, Tag, ConfigProvider, Button, Row, Col, Card, Input } from 'antd';
+import {
+ PlusOutlined,
+ EditOutlined,
+ DeleteOutlined,
+ EyeOutlined,
+ SearchOutlined,
+} from '@ant-design/icons';
+import { NotifAlert, NotifConfirmDialog } from '../../../../components/Global/ToastNotif';
+import { useNavigate } from 'react-router-dom';
+import TableList from '../../../../components/Global/TableList';
+
+// Dummy data
+const initialTagsData = [
+ {
+ tag_id: 1,
+ tag_code: 'J06-OPEN',
+ tag_name: 'tag 1',
+ tag_value: 'On',
+ tag_type: 'alarm',
+ tag_description: 'test1',
+ },
+ {
+ tag_id: 2,
+ tag_code: 'J06-CLS',
+ tag_name: 'tag 2',
+ tag_value: 'Off',
+ tag_type: 'alarm',
+ tag_description: 'tes2',
+ },
+ {
+ tag_id: 3,
+ tag_code: 'J06-VRS',
+ tag_name: 'tag 3',
+ tag_value: 'Critical',
+ tag_type: 'measurement',
+ tag_description: 'test3',
+ },
+ {
+ tag_id: 4,
+ tag_code: 'J07-IR',
+ tag_name: 'tag 4 R',
+ tag_value: 'On',
+ tag_type: 'measurement',
+ tag_description: 'test 4',
+ },
+ {
+ tag_id: 5,
+ tag_code: 'K01-CLS',
+ tag_name: 'tag 5',
+ tag_value: 'Off',
+ tag_type: 'alarm',
+ tag_description: 'tes5',
+ },
+ {
+ tag_id: 6,
+ tag_code: 'J07-IS',
+ tag_name: 'tag6',
+ tag_value: 'On',
+ tag_type: 'measurement',
+ tag_description: 'tes6',
+ },
+ {
+ tag_id: 7,
+ tag_code: 'J07-IT',
+ tag_name: 'tag7',
+ tag_value: 'Critical',
+ tag_type: 'measurement',
+ tag_description: 'tes7',
+ },
+ {
+ tag_id: 8,
+ tag_code: 'J06-VST',
+ tag_name: 'tag8',
+ tag_value: 'On',
+ tag_type: 'measurement',
+ tag_description: 'tes8',
+ },
+ {
+ tag_id: 9,
+ tag_code: 'J06-VTR',
+ tag_name: 'tag9',
+ tag_value: 'On',
+ tag_type: 'measurement',
+ tag_description: 'tes9',
+ },
+ {
+ tag_id: 10,
+ tag_code: 'K02-OPEN',
+ tag_name: 'tag10',
+ tag_value: 'On',
+ tag_type: 'alarm',
+ tag_description: 'tes10',
+ },
+ {
+ tag_id: 11,
+ tag_code: 'K02-CLS',
+ tag_name: 'Kontaktor K02 Close',
+ tag_value: 'Off',
+ tag_type: 'alarm',
+ tag_description: 'Kontaktor K02 dalam kondisi tertutup',
+ },
+ {
+ tag_id: 12,
+ tag_code: 'J08-FREQ',
+ tag_name: 'Frequency',
+ tag_value: 'On',
+ tag_type: 'measurement',
+ tag_description: 'Frekuensi sistem normal (50Hz)',
+ },
+ {
+ tag_id: 13,
+ tag_code: 'J08-PF',
+ tag_name: 'Power Factor',
+ tag_value: 'Critical',
+ tag_type: 'measurement',
+ tag_description: 'Power factor rendah, perlu perbaikan',
+ },
+ {
+ tag_id: 14,
+ tag_code: 'TR01-TEMP',
+ tag_name: 'Transformer Temperature',
+ tag_value: 'Critical',
+ tag_type: 'alarm',
+ tag_description: 'Suhu trafo melebihi batas aman',
+ },
+ {
+ tag_id: 15,
+ tag_code: 'TR01-OIL',
+ tag_name: 'Transformer Oil Level',
+ tag_value: 'On',
+ tag_type: 'alarm',
+ tag_description: 'Level oli trafo normal',
+ },
+ {
+ tag_id: 16,
+ tag_code: 'GEN01-RUN',
+ tag_name: 'Generator Running',
+ tag_value: 'Off',
+ tag_type: 'alarm',
+ tag_description: 'Generator dalam kondisi mati',
+ },
+ {
+ tag_id: 17,
+ tag_code: 'GEN01-FUEL',
+ tag_name: 'Generator Fuel Level',
+ tag_value: 'On',
+ tag_type: 'measurement',
+ tag_description: 'Level bahan bakar generator mencukupi',
+ },
+ {
+ tag_id: 18,
+ tag_code: 'UPS01-BAT',
+ tag_name: 'UPS Battery Status',
+ tag_value: 'On',
+ tag_type: 'alarm',
+ tag_description: 'Status battery UPS normal',
+ },
+ {
+ tag_id: 19,
+ tag_code: 'UPS01-LOAD',
+ tag_name: 'UPS Load',
+ tag_value: 'Critical',
+ tag_type: 'measurement',
+ tag_description: 'Beban UPS mendekati kapasitas maksimal',
+ },
+ {
+ tag_id: 20,
+ tag_code: 'PANEL-DOOR',
+ tag_name: 'Panel Door Status',
+ tag_value: 'Off',
+ tag_type: 'alarm',
+ tag_description: 'Pintu panel tertutup',
+ },
+];
+
+const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [
+ {
+ title: 'ID',
+ dataIndex: 'tag_id',
+ key: 'tag_id',
+ width: '5%',
+ hidden: 'true',
+ },
+ {
+ title: 'Tag Code',
+ dataIndex: 'tag_code',
+ key: 'tag_code',
+ width: '10%',
+ },
+ {
+ title: 'Tag Name',
+ dataIndex: 'tag_name',
+ key: 'tag_name',
+ width: '20%',
+ },
+ {
+ title: 'Value',
+ dataIndex: 'tag_value',
+ key: 'tag_value',
+ width: '10%',
+ align: 'center',
+ render: (_, { tag_value }) => {
+ let color = 'default';
+ if (tag_value.toLowerCase() === 'on') color = 'green';
+ else if (tag_value.toLowerCase() === 'off') color = 'gray';
+ else if (tag_value.toLowerCase() === 'critical') color = 'red';
+ return (
+
+ {tag_value}
+
+ );
+ },
+ },
+ {
+ title: 'Type',
+ dataIndex: 'tag_type',
+ key: 'tag_type',
+ width: '10%',
+ },
+ {
+ title: 'Description',
+ dataIndex: 'tag_description',
+ key: 'tag_description',
+ width: '25%',
+ },
+ {
+ title: 'Aksi',
+ key: 'aksi',
+ 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 ListTag = memo(function ListTag(props) {
+ const [showFilter, setShowFilter] = useState(false);
+ const [trigerFilter, setTrigerFilter] = useState(false);
+ const [tagsData, setTagsData] = useState(initialTagsData);
+
+ const defaultFilter = { search: '' };
+ const [formDataFilter, setFormDataFilter] = useState(defaultFilter);
+ const [searchValue, setSearchValue] = useState('');
+
+ const navigate = useNavigate();
+
+ // Dummy data function to simulate API call - now uses state
+ const getAllTag = async (params) => {
+ // Simulate API delay
+ await new Promise((resolve) => setTimeout(resolve, 300));
+
+ // Extract URLSearchParams - TableList sends URLSearchParams object
+ const searchParam = params.get('search') || '';
+ const page = parseInt(params.get('page')) || 1;
+ const limit = parseInt(params.get('limit')) || 10;
+
+ console.log('getAllTag called with:', { searchParam, page, limit });
+
+ // Filter by search
+ let filteredTags = tagsData;
+ if (searchParam) {
+ const searchLower = searchParam.toLowerCase();
+ filteredTags = tagsData.filter(
+ (tag) =>
+ tag.tag_code.toLowerCase().includes(searchLower) ||
+ tag.tag_name.toLowerCase().includes(searchLower) ||
+ tag.tag_type.toLowerCase().includes(searchLower) ||
+ tag.tag_description.toLowerCase().includes(searchLower)
+ );
+ }
+
+ // Pagination logic
+ const totalData = filteredTags.length;
+ const totalPages = Math.ceil(totalData / limit);
+ const startIndex = (page - 1) * limit;
+ const endIndex = startIndex + limit;
+ const paginatedData = filteredTags.slice(startIndex, endIndex);
+
+ // Return structure that matches TableList expectation
+ // Based on TableList code:
+ // - resData.data.data = array of items
+ // - resData.data.total = total count
+ // - resData.data.paging.page = current page
+ // - resData.data.paging.limit = items per page
+ // - resData.data.paging.total = total items
+ // - resData.data.paging.page_total = total pages
+ return {
+ status: 200,
+ statusCode: 200,
+ data: {
+ data: paginatedData,
+ total: totalData,
+ paging: {
+ page: page,
+ limit: limit,
+ total: totalData,
+ page_total: totalPages,
+ },
+ },
+ };
+ };
+
+ useEffect(() => {
+ const token = localStorage.getItem('token');
+ if (token) {
+ if (props.actionMode == 'list') {
+ setFormDataFilter(defaultFilter);
+ doFilter();
+ }
+ } else {
+ navigate('/signin');
+ }
+ }, [props.actionMode, tagsData]);
+
+ const toggleFilter = () => {
+ setFormDataFilter(defaultFilter);
+ setShowFilter((prev) => !prev);
+ };
+
+ const doFilter = () => {
+ setTrigerFilter((prev) => !prev);
+ };
+
+ const handleSearch = () => {
+ setFormDataFilter({ search: searchValue });
+ setTrigerFilter((prev) => !prev);
+ };
+
+ const handleSearchClear = () => {
+ setSearchValue('');
+ setFormDataFilter({ search: '' });
+ setTrigerFilter((prev) => !prev);
+ };
+
+ const showPreviewModal = (param) => {
+ props.setSelectedData(param);
+ props.setActionMode('preview');
+ };
+
+ const showEditModal = (param = null) => {
+ props.setSelectedData(param);
+ props.setActionMode('edit');
+ };
+
+ const showAddModal = (param = null) => {
+ props.setSelectedData(param);
+ props.setActionMode('add');
+ };
+
+ const showDeleteDialog = (param) => {
+ NotifConfirmDialog({
+ icon: 'question',
+ title: 'Konfirmasi',
+ message: 'Apakah anda yakin hapus data "' + param.tag_name + '" ?',
+ onConfirm: () => handleDelete(param.tag_id),
+ onCancel: () => props.setSelectedData(null),
+ });
+ };
+
+ const handleDelete = async (tag_id) => {
+ // Find tag name before deleting
+ const tagToDelete = tagsData.find((tag) => tag.tag_id === tag_id);
+
+ // Simulate delete API call
+ await new Promise((resolve) => setTimeout(resolve, 300));
+
+ // Remove from state
+ const updatedTags = tagsData.filter((tag) => tag.tag_id !== tag_id);
+ setTagsData(updatedTags);
+
+ NotifAlert({
+ icon: 'success',
+ title: 'Berhasil',
+ message: `Data Tag "${tagToDelete?.tag_name || ''}" berhasil dihapus.`,
+ });
+ };
+
+ return (
+
+
+
+
+
+
+ {
+ const value = e.target.value;
+ setSearchValue(value);
+ // Auto search when clearing by backspace/delete
+ if (value === '') {
+ setFormDataFilter({ search: '' });
+ setTrigerFilter((prev) => !prev);
+ }
+ }}
+ onSearch={handleSearch}
+ allowClear={{
+ clearIcon: ✕,
+ }}
+ enterButton={
+ }
+ style={{
+ backgroundColor: '#23A55A',
+ borderColor: '#23A55A',
+ }}
+ >
+ Search
+
+ }
+ size="large"
+ />
+
+
+
+
+ }
+ onClick={() => showAddModal()}
+ size="large"
+ >
+ Tambah Data
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+});
+
+export default ListTag;