diff --git a/public/media/avatars/profile.png b/public/media/avatars/profile.png new file mode 100644 index 0000000..fc1169b Binary files /dev/null and b/public/media/avatars/profile.png differ diff --git a/src/layouts/demo2/header/HeaderTopbar.tsx b/src/layouts/demo2/header/HeaderTopbar.tsx index 5ef3aab..485311c 100644 --- a/src/layouts/demo2/header/HeaderTopbar.tsx +++ b/src/layouts/demo2/header/HeaderTopbar.tsx @@ -15,7 +15,7 @@ const HeaderTopbar = () => { const handleDropdownChatShow = () => { window.dispatchEvent(new Event('resize')); }; - console.log(getAuth()) + // console.log(getAuth()) return (
@@ -43,7 +43,7 @@ const HeaderTopbar = () => { User avatar diff --git a/src/pages/dashboards/home/DashboardHomePage.tsx b/src/pages/dashboards/home/DashboardHomePage.tsx index 22d69b9..5c90b52 100644 --- a/src/pages/dashboards/home/DashboardHomePage.tsx +++ b/src/pages/dashboards/home/DashboardHomePage.tsx @@ -45,7 +45,7 @@ const DashboardHomePage = () => { useEffect(() => { if (selectYear.length > 0 && !selectedYear) { let latestYear = Math.max(...selectYear.map((item) => parseInt(item, 10))).toString(); - console.log('latestYear :', latestYear); + // console.log('latestYear :', latestYear); setSelectedYear(latestYear); setInitialYear(latestYear); } diff --git a/src/pages/members/feedback-member/blocks/FeedbackDetail.tsx b/src/pages/members/feedback-member/blocks/FeedbackDetail.tsx new file mode 100644 index 0000000..648ac9e --- /dev/null +++ b/src/pages/members/feedback-member/blocks/FeedbackDetail.tsx @@ -0,0 +1,199 @@ +import React, { useEffect, useState } from 'react'; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { useCallApi } from '@/hooks'; +import { apiConfig } from '@/config/api.config'; +import { format } from 'date-fns'; + +interface FeedbackDetailProps { + showDialog: boolean; + handleDialog: (show: boolean) => void; + feedbackId: string | null; +} + +interface FeedbackDetailData { + Feedback_id: string; + Feedback_feedback_notes: string; + Feedback_feedback_screenshoot: string; + Feedback_review_notes: string; + Feedback_emotion: string; + Feedback_review_screenshoot: string | null; + Feedback_category: string; + Feedback_status: string; + Feedback_created_at: string; + Feedback_review_by: string | null; + Feedback_review_at: string; + Feedback_deleted_by: string | null; + Feedback_deleted_at: string | null; + Feedback_createdById: string; +} + +const API_URL = apiConfig.service_feedback; + +const FeedbackDetail: React.FC = ({ showDialog, handleDialog, feedbackId }) => { + const [feedbackDetail, setFeedbackDetail] = useState(null); + const [loading, setLoading] = useState(false); + const { GetData } = useCallApi(); + + useEffect(() => { + const fetchFeedbackDetail = async () => { + if (!feedbackId) return; + + setLoading(true); + try { + const response = await GetData(`${API_URL}/feedback/${feedbackId}`,{}); + if (response?.data) { + setFeedbackDetail(response.data); + } + } catch (error) { + console.error('Error fetching feedback detail:', error); + } finally { + setLoading(false); + } + }; + + if (showDialog && feedbackId) { + fetchFeedbackDetail(); + } else { + setFeedbackDetail(null); + } + }, [showDialog, feedbackId, GetData]); + + const formatDate = (dateString: string | null) => { + if (!dateString) return '-'; + try { + return format(new Date(dateString), 'yyyy-MM-dd HH:mm:ss'); + } catch (e) { + return dateString; + } + }; + + const getEmotionText = (emotion: string) => { + const emotions: Record = { + '1': 'Very Disappointed', + '2': 'Disappointed', + '3': 'Happy', + '4': 'Very Happy', + '5': 'Extremely Happy' + }; + return emotions[emotion] || emotion; + }; + + const getStatusText = (status: string) => { + const statuses: Record = { + 'W': 'Waiting', + 'P': 'Processed', + 'D': 'Done' + }; + return statuses[status] || status; + }; + + return ( + + + + Feedback Detail + + + {loading ? ( +
+
+ Loading... +
+
+ ) : feedbackDetail ? ( +
+
+
+ +
{feedbackDetail.Feedback_id}
+
+ +
+ +
{feedbackDetail.Feedback_category}
+
+ +
+ +
{getEmotionText(feedbackDetail.Feedback_emotion)}
+
+ +
+ +
{getStatusText(feedbackDetail.Feedback_status)}
+
+ +
+ +
{formatDate(feedbackDetail.Feedback_created_at)}
+
+ +
+ +
{feedbackDetail.Feedback_createdById}
+
+
+ +
+ +
{feedbackDetail.Feedback_feedback_notes}
+
+ + {feedbackDetail.Feedback_feedback_screenshoot && ( +
+ +
+ Feedback Screenshot { + (e.target as HTMLImageElement).style.display = 'none'; + (e.target as HTMLImageElement).parentElement?.appendChild( + Object.assign(document.createElement('div'), { + className: 'text-sm text-gray-500', + textContent: 'Image not available or invalid URL' + }) + ); + }} + /> +
+
+ )} + +
+ +
{feedbackDetail.Feedback_review_notes || '-'}
+
+ + {feedbackDetail.Feedback_review_screenshoot && ( +
+ +
+ Review Screenshot { + (e.target as HTMLImageElement).style.display = 'none'; + (e.target as HTMLImageElement).parentElement?.appendChild( + Object.assign(document.createElement('div'), { + className: 'text-sm text-gray-500', + textContent: 'Image not available or invalid URL' + }) + ); + }} + /> +
+
+ )} +
+ ) : ( +
No feedback details found
+ )} +
+
+ ); +}; + +export default FeedbackDetail; \ No newline at end of file diff --git a/src/pages/members/feedback-member/hooks/ManageFeedbackMemberContext.tsx b/src/pages/members/feedback-member/hooks/ManageFeedbackMemberContext.tsx index 41f986e..d971d08 100644 --- a/src/pages/members/feedback-member/hooks/ManageFeedbackMemberContext.tsx +++ b/src/pages/members/feedback-member/hooks/ManageFeedbackMemberContext.tsx @@ -4,7 +4,8 @@ import { useCallApi } from '@/hooks'; import { ColumnDef } from '@tanstack/react-table'; import React, { createContext, useCallback, useMemo, useState } from 'react'; import { Toaster } from 'sonner'; -// import ListToolbar from '../blocks/ListToolbar'; +// import FeedbackDetail from './blocks/FeedbackDetail'; +import FeedbackDetail from '../blocks/FeedbackDetail'; interface feedbackProps { id: string; @@ -24,6 +25,8 @@ interface ContextProps { handleAddDialog: (show: boolean) => void; showDeleteDialog: boolean; handleDeleteDialog: (show: boolean, selected_sucos: string | null) => void; + showDetailDialog: boolean; + handleDetailDialog: (show: boolean, selected_sucos: string | null) => void; selectedfeedback: string | null; getfeedbackLists: ( limit: number, @@ -42,6 +45,8 @@ const initialProps: ContextProps = { handleAddDialog: () => {}, showDeleteDialog: false, handleDeleteDialog: () => {}, + showDetailDialog: false, + handleDetailDialog: () => {}, selectedfeedback: null, getfeedbackLists: async () => undefined }; @@ -54,6 +59,7 @@ const ManageFeedbackMemberProvider = ({ children }: { children: React.ReactNode const [showAddDialog, setShowAddDialog] = useState(false); const [showEditDialog, setShowEditDialog] = useState(false); const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const [showDetailDialog, setShowDetailDialog] = useState(false); const [selectedfeedback, setSelectedfeedback] = useState(null); const { GetData } = useCallApi(); @@ -71,10 +77,35 @@ const ManageFeedbackMemberProvider = ({ children }: { children: React.ReactNode setSelectedfeedback(show ? selected_feedback : null); }, []); + const handleDetailDialog = useCallback((show: boolean, selected_feedback: string | null) => { + setShowDetailDialog(show); + setSelectedfeedback(show ? selected_feedback : null); + }, []); + const columns = useMemo[]>( () => [ { - accessorFn: (row) => row.feedback_notes, + accessorFn: (row) => row.customer, + id: 'customer_id', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + accessorFn: (row) => row.Feedback_emotion, + id: 'feedback_emotion', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + accessorFn: (row) => row.Feedback_feedback_notes, id: 'feedback_notes', header: ({ column }) => , enableSorting: false, @@ -84,67 +115,45 @@ const ManageFeedbackMemberProvider = ({ children }: { children: React.ReactNode } }, { - accessorFn: (row) => row.feedback_screenshot, - id: 'feedback_screenshot', - header: ({ column }) => , + id: 'actions', + header: ({ column }) => , enableSorting: false, enableHiding: false, + cell: (data) => { + const row = data.row.original; + return ( + <> + + {/* + */} + + ); + }, meta: { - headerClassName: 'w-[250px]' - } - }, - { - accessorFn: (row) => row.review_notes, - id: 'review_notes', - header: ({ column }) => , - enableSorting: false, - enableHiding: false, - meta: { - headerClassName: 'w-[250px]' - } - }, - { - accessorFn: (row) => row.review_screenshot, - id: 'review_screenshot', - header: ({ column }) => , - enableSorting: false, - enableHiding: false, - meta: { - headerClassName: 'w-[250px]' + headerClassName: 'w-[150px] text-center', + cellClassName: 'text-center' } } - // , - // { - // id: 'actions', - // header: ({ column }) => , - // enableSorting: false, - // enableHiding: false, - // cell: (data) => { - // const row = data.row.original; - // return ( - // <> - // - // - // - // ); - // }, - // meta: { - // headerClassName: 'w-[100px] text-center', - // cellClassName: 'text-center' - // } - // } ], - [handleEditDialog, handleDeleteDialog] + [handleEditDialog, handleDeleteDialog, handleDetailDialog] ); const getfeedbackLists = async (page: number, limit: number, sorting: any, filter: any) => { @@ -167,7 +176,6 @@ const ManageFeedbackMemberProvider = ({ children }: { children: React.ReactNode } }; - return ( + + {/* Feedback Detail Modal Component */} + handleDetailDialog(show, show ? selectedfeedback : null)} + feedbackId={selectedfeedback} + /> { createMember.pin = 'admin'; delete createMember.password; delete createMember.try_pin; - delete createMember.license_number; delete createMember.isneedapproval; delete createMember.isapproved; delete createMember.approveddate; diff --git a/src/pages/members/manage-members/blocks/DetailMember.tsx b/src/pages/members/manage-members/blocks/DetailMember.tsx index 1ba2005..aa0a951 100644 --- a/src/pages/members/manage-members/blocks/DetailMember.tsx +++ b/src/pages/members/manage-members/blocks/DetailMember.tsx @@ -70,7 +70,8 @@ const DetailMember = ({ showAddDialog, setShowAddDialog, handleSubmit, initialDa setFormData({ ...formData, [name]: value }); } else { if (name === 'municipio_id' || name === 'posto_adms_id' || name === 'suco_id') await getMasterAfter(name, value); - setFormData({ ...formData, [name]: value }); + if (name==='msisdn') setFormData({ ...formData, [name]: value.replace(/\D/g, '') }) + else setFormData({ ...formData, [name]: value }); } }; diff --git a/src/pages/transaction/approval-transaction/blocks/ApprovalDialog.tsx b/src/pages/transaction/approval-transaction/blocks/ApprovalDialog.tsx index 2371696..03f8069 100644 --- a/src/pages/transaction/approval-transaction/blocks/ApprovalDialog.tsx +++ b/src/pages/transaction/approval-transaction/blocks/ApprovalDialog.tsx @@ -26,7 +26,7 @@ const API_URL = apiConfig.transaction; const ApprovalDialog = () => { const { GetData, PostData } = useCallApi(); - // const { table, reload } = useDataGrid(); + const { reload } = useDataGrid(); const { showApprovalDialog, @@ -85,7 +85,7 @@ const ApprovalDialog = () => { }; doSaveLogActivity(createActivity); setShowApprovalDialog(false); - // reload(); + reload(); } else { setAlert({ show: true, message: response?.message }); } diff --git a/src/pages/transaction/approval-transaction/blocks/DetailApprovalTransaction.tsx b/src/pages/transaction/approval-transaction/blocks/DetailApprovalTransaction.tsx index 755580a..7ddb01c 100644 --- a/src/pages/transaction/approval-transaction/blocks/DetailApprovalTransaction.tsx +++ b/src/pages/transaction/approval-transaction/blocks/DetailApprovalTransaction.tsx @@ -42,7 +42,15 @@ const DetailApprovalTransaction = () => { } }, [showDetailDialog, selectedTransactionId, GetData]); - const [activeTab, setActiveTab] = useState('detail'); // 'detail', 'log', 'approve' + const [activeTab, setActiveTab] = useState('detail'); + + // Reset ke tab awal saat dialog dibuka + useEffect(() => { + if (showDetailDialog) { + setActiveTab('detail'); + } + }, [showDetailDialog]); + return ( diff --git a/src/pages/transaction/approval-transaction/hooks/ApprovalTransactionContext.tsx b/src/pages/transaction/approval-transaction/hooks/ApprovalTransactionContext.tsx index ba57c71..163055a 100644 --- a/src/pages/transaction/approval-transaction/hooks/ApprovalTransactionContext.tsx +++ b/src/pages/transaction/approval-transaction/hooks/ApprovalTransactionContext.tsx @@ -326,8 +326,6 @@ const ApprovalTransactionProvider = ({ children }: { children: React.ReactNode } }} > - - + + {children} diff --git a/src/pages/transaction/history-transaction/Transaction.tsx b/src/pages/transaction/history-transaction/Transaction.tsx index ca2dbf8..ee0aeff 100644 --- a/src/pages/transaction/history-transaction/Transaction.tsx +++ b/src/pages/transaction/history-transaction/Transaction.tsx @@ -29,7 +29,7 @@ const Transaction = () => {
- + {/* */} diff --git a/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx b/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx index 079aaf6..3738a77 100644 --- a/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx +++ b/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx @@ -1,12 +1,14 @@ import { useTransactionContext } from '../hooks/useTransactionContext'; import { useCallApi } from '@/hooks'; import { apiConfig } from '@/config/api.config'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; +import ResendTransaction from './ResendTransaction'; import { Dialog, DialogBody, DialogContent, DialogDescription, + DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; @@ -23,6 +25,16 @@ const DetailTransaction = () => { const [transactionDetails, setTransactionDetails] = useState(null); + const [showResend, setResend] = useState(false); + + const handleResendOpen = () => { + setResend(true); + }; + + const handleResendClose = () => { + setResend(false); + }; + useEffect(() => { const fetchTransactionDetails = async () => { if (selectedTransactionId) { @@ -42,7 +54,15 @@ const DetailTransaction = () => { } }, [showDetailDialog, selectedTransactionId, GetData]); - const [activeTab, setActiveTab] = useState('detail'); // 'detail', 'log', 'approve' + const [activeTab, setActiveTab] = useState('detail'); + + // Reset ke tab awal saat dialog dibuka + useEffect(() => { + if (showDetailDialog) { + setActiveTab('detail'); + } + }, [showDetailDialog]); + return ( @@ -622,10 +642,20 @@ const DetailTransaction = () => { )}
+ + {/* {showResendDialog} */} + {(transactionDetails?.status === 'F' || transactionDetails?.status === 'P') && transactionDetails?.status_approve !== 'W' && ( +
+ + +
+ )} + + ); }; -export default DetailTransaction; +export default DetailTransaction; \ No newline at end of file diff --git a/src/pages/transaction/history-transaction/blocks/ResendTransaction.tsx b/src/pages/transaction/history-transaction/blocks/ResendTransaction.tsx index 2b4b13d..3da5a09 100644 --- a/src/pages/transaction/history-transaction/blocks/ResendTransaction.tsx +++ b/src/pages/transaction/history-transaction/blocks/ResendTransaction.tsx @@ -19,10 +19,18 @@ import { Input } from '@/components/ui/input'; const API_URL = apiConfig.transaction; -const ResendTransaction = () => { - const { showResendDialog, handleResendDialog, selectedTransactionForResend } = useTransactionContext(); +interface ResendTransactionProps { + isOpen: boolean | null; + onClose: () => void | null; + selectedTransactionForResend: any | null; // Adjust the type as per your requirements +} + +const ResendTransaction = ({ isOpen, onClose, selectedTransactionForResend }: ResendTransactionProps) => { + + + // const { showResendDialog, handleResendDialog, selectedTransactionForResend } = useTransactionContext(); const { GetData, PostData } = useCallApi(); - const { reload } = useDataGrid(); + // const { reload } = useDataGrid(); const [transactionDetails, setTransactionDetails] = useState(null); const [alert, setAlert] = useState({ show: false, @@ -34,38 +42,42 @@ const ResendTransaction = () => { const [formField, setFormField] = useState(initialStatePin); useEffect(() => { - if (showResendDialog) { + // if (showResendDialog) { + if (isOpen) { // Reset form fields when dialog opens setFormField({ pin: '' }); setAlert({ show: false, message: '' }); - setTransactionDetails(null); // Optional reset + // setTransactionDetails(null); // Optional reset } - }, [showResendDialog]); + }, [isOpen]); - useEffect(() => { - const fetchTransactionDetails = async () => { - if (selectedTransactionForResend) { - try { - const response = await GetData( - `${API_URL}/transaction/history/detail/${selectedTransactionForResend}`, - { - id: selectedTransactionForResend, - } - ); - setTransactionDetails(response?.data); - } catch (error) { - console.error('Error fetching transaction', error); - } - } + // useEffect(() => { + // const fetchTransactionDetails = async () => { + // if (selectedTransactionForResend) { + // try { + // const response = await GetData( + // `${API_URL}/transaction/history/detail/${selectedTransactionForResend}`, + // { + // id: selectedTransactionForResend, + // } + // ); + // setTransactionDetails(response?.data); + // } catch (error) { + // console.error('Error fetching transaction', error); + // } + // } - }; + // }; + + // if (isOpen && selectedTransactionForResend) { + // fetchTransactionDetails(); + // } + // }, [isOpen, selectedTransactionForResend, GetData]); + + // console.log(selectedTransactionForResend); - if (showResendDialog && selectedTransactionForResend) { - fetchTransactionDetails(); - } - }, [showResendDialog, selectedTransactionForResend, GetData]); const doResendTransaction = useCallback(async (data: any | null, pintransactiion: string) => { @@ -146,9 +158,9 @@ const ResendTransaction = () => { if (response?.status) { setAlert({ show: false, message: '' }); - handleResendDialog(false, null); + // handleResendDialog(false, null); toast.success('Success Retry Transaction'); - reload(); + // reload(); const createActivity = { module: 'History Transaction', @@ -161,17 +173,20 @@ const ResendTransaction = () => { setAlert({ show: true, message: response?.message }); toast.error('Failed Retry Transaction'); } - }, [selectedTransactionForResend, PostData, handleResendDialog, reload]); + }, [selectedTransactionForResend, PostData]); + + if (!isOpen) return null; + return ( - handleResendDialog(open, null)}> +

Are you sure?

- You will retry this transaction! +

You will retry this transaction!

{alert.show && ( @@ -188,14 +203,16 @@ const ResendTransaction = () => { type="password" value={formField.pin} onChange={(e) => setFormField({ ...formField, pin: e.target.value })} - /> + /> + - + {/* - */} + +
diff --git a/src/pages/transaction/history-transaction/hooks/TransactionContext.tsx b/src/pages/transaction/history-transaction/hooks/TransactionContext.tsx index 8d7bbdc..569e266 100644 --- a/src/pages/transaction/history-transaction/hooks/TransactionContext.tsx +++ b/src/pages/transaction/history-transaction/hooks/TransactionContext.tsx @@ -2,16 +2,11 @@ import { DataGridColumnHeader, DataGridProvider, KeenIcon } from '@/components'; import { Toaster } from '@/components/ui/sonner'; import { apiConfig } from '@/config/api.config'; import { ColumnDef } from '@tanstack/react-table'; -import axios from 'axios'; -import React, { createContext, useCallback, useEffect, useMemo, useState } from 'react'; +import React, { createContext, useCallback, useMemo, useState } from 'react'; import { useCallApi } from '@/hooks'; import ListToolbar from '../blocks/ListToolbar'; -import { Button } from '@/components/ui/button'; import { useNavigate } from 'react-router'; import DetailTransaction from '../blocks/DetailTransaction'; -import { log } from 'console'; -import ResendTransaction from '../blocks/ResendTransaction'; -import { comment } from 'stylis'; interface TransactionProps { id: number; @@ -31,9 +26,6 @@ interface ContextProps { setShowDetailDialog: React.Dispatch>; selectedTransactionId: number | null; setSelectedTransactionId: React.Dispatch>; - showResendDialog: boolean; - handleResendDialog: (show: boolean, selected_transaction: string | null) => void; - selectedTransactionForResend: string | null; } const initialProps: ContextProps = { @@ -41,10 +33,7 @@ const initialProps: ContextProps = { showDetailDialog: false, setShowDetailDialog: () => { }, selectedTransactionId: null, - setSelectedTransactionId: () => { }, - showResendDialog: false, - handleResendDialog: (show: boolean, selected_transaction: string | null) => { }, - selectedTransactionForResend: null + setSelectedTransactionId: () => { } }; const ManageTransactionContext = createContext(initialProps); @@ -59,13 +48,6 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => { const handleNavigate = (path: string) => { const url = navigate(`${API_URL}/transaction/history/${path}`); }; - const [showResendDialog, setShowResendDialog] = useState(false); - const [selectedTransactionForResend, setSelectedTransactionForResend] = useState(null); - - const handleResendDialog = useCallback((show: boolean, selected_transaction: string | null) => { - setSelectedTransactionForResend(show ? selected_transaction : null); - setShowResendDialog(show); - }, []); const columns = useMemo[]>( @@ -236,18 +218,6 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => { > - {/* add new button for resend transaction failed */} - {isVisible && - - }
); }, @@ -257,7 +227,7 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => { } } ], - [handleResendDialog]); + []); const getTransactionLists = async (page: number, limit: number, sorting: any, filter: any) => { try { @@ -346,10 +316,7 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => { showDetailDialog, setShowDetailDialog, selectedTransactionId, - setSelectedTransactionId, - showResendDialog, - handleResendDialog, - selectedTransactionForResend + setSelectedTransactionId }} >