From 039b4d05e07807d02f5d351bb61e473f6bcdb04c Mon Sep 17 00:00:00 2001 From: bagusajisaputroo Date: Tue, 22 Apr 2025 00:51:05 +0700 Subject: [PATCH] updating feedback member --- .../feedback-member/blocks/FeedbackDetail.tsx | 199 ++++++++++++++++++ .../hooks/ManageFeedbackMemberContext.tsx | 137 ++++++------ 2 files changed, 276 insertions(+), 60 deletions(-) create mode 100644 src/pages/members/feedback-member/blocks/FeedbackDetail.tsx 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..5d746be 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} + />