이호영 이호영 2025-02-04
발송결과 상세 진행중
@1230dea6acb83a10cc2da1b4316f3cde7c05afd9
src/main/java/itn/let/mjo/msgsent/service/MjonMsgDetailSentVO.java
--- src/main/java/itn/let/mjo/msgsent/service/MjonMsgDetailSentVO.java
+++ src/main/java/itn/let/mjo/msgsent/service/MjonMsgDetailSentVO.java
@@ -16,8 +16,10 @@
 	
 	private String msgGroupId;
 	private String reqDate;
+	private String regDate;
 	private String msgGroupCnt;
 	private String reserveYn;
+	private String reserveCYn;
 	private String callFrom;
 	private String userId;
 	private String smsTxt;
@@ -25,6 +27,7 @@
 	private String msgType;
 	private String fileCnt;
 	private String msgKind;
+	private String eachPrice;
 	private String sentDate;
 	private String filePath1;
 	private String filePath2;
@@ -33,6 +36,24 @@
 	private String callTo;
 	private String statusTxt;
 	private String addrNm;
+	
+	
+	private String resultSValue;
+	private String resultFValue;
+	private String resultWValue;
+	
+	private String successPct;
+	private String failedPct;
+	private String waitingPct;
+	
+	
+	
+	
+	
+	private String statusCd; // 진행상태 코드
+	private String divideYN;
+	private int diffMin;
+	private String totPrice;
 
 
 	// FileInfo 리스트 필드 추가
src/main/java/itn/let/mjo/msgsent/service/MjonMsgSentVO.java
--- src/main/java/itn/let/mjo/msgsent/service/MjonMsgSentVO.java
+++ src/main/java/itn/let/mjo/msgsent/service/MjonMsgSentVO.java
@@ -4,11 +4,17 @@
 import java.util.List;
 
 import itn.let.uss.umt.service.UserDefaultVO;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
 import lombok.Getter;
+import lombok.NoArgsConstructor;
 import lombok.Setter;
 
 @Getter
 @Setter
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
 public class MjonMsgSentVO extends UserDefaultVO{
 
 	private static final long serialVersionUID = 1L;
src/main/java/itn/let/mjo/msgsent/service/impl/MjonMsgSentServiceImpl.java
--- src/main/java/itn/let/mjo/msgsent/service/impl/MjonMsgSentServiceImpl.java
+++ src/main/java/itn/let/mjo/msgsent/service/impl/MjonMsgSentServiceImpl.java
@@ -127,7 +127,6 @@
 		Map<String, Object> resultMap = new HashMap<String, Object>();
 		
 		System.out.println("mjonMsgSentVO.getSearchConditionSite() :: "+ mjonMsgSentVO.getSearchConditionSite());
-		
 		// 목록
 		List<MjonMsgSentVO> resultList = mjonMsgSentDAO.selectAllMsgSentList_advc(mjonMsgSentVO);
 
@@ -136,36 +135,11 @@
 		
 		resultList = makeDetailFunction(resultList);
 
-		// 공통코드 ITN057에 대한 코드화 진행
 		/*
-		 * CODE_ID	CODE	CODE_NM		CODE_DC
-		 * ITN057	01		진행중		진행중
-		 * ITN057	02		완료			완료출해야함
-		 * ITN057	03		예약대기		예약대기(발송전) 버튼으로 노출해야함
-		 * ITN057	04		-			예약취소 ( - 으로 노출 )
+		 * 진행상태 코드화
 		 * */
 		resultList.stream().forEach(t->{
-
-			String code;
-			
-			log.info(" + t.getDiffMin() :: [{}]", t.getDiffMin());
-			log.info(" + t.getDiffMin() :: [{}]", t.getDiffMin() > -5);
-			if ("Y".equals(t.getReserveCYn())) {
-				code = "04"; // 예약취소 코드
-			} else if (
-					"Y".equals(t.getReserveYn()) 
-						&& "N".equals(t.getReserveCYn()) 
-						&& t.getMsgGroupCnt().equals(t.getResultWValue())
-						&& t.getDiffMin() < -5 // 예약 시간이 5분 이상인 것들만
-						) {
-				code = "03"; // 예약대기 코드 ( 예약취소 버튼 노출 )
-			} else if (t.getMsgGroupCnt().equals(t.getResultSValue()) || t.getMsgGroupCnt().equals(t.getResultFValue())) {
-				code = "02"; // 완료 코드
-			} else {
-				code = "01"; // 진행중 코드
-			}
-			
-			
+			String code = getStatusCode(t);
 			t.setStatusCd(code);
 		});
 
@@ -187,25 +161,61 @@
 		Map<String, Object> resultMap = new HashMap<String, Object>();
 
 		// 목록
-		MjonMsgDetailSentVO result = mjonMsgSentDAO.selectAllMsgSentDetailView(mjonMsgDetailSentVO);
+		MjonMsgDetailSentVO resultVO = mjonMsgSentDAO.selectAllMsgSentDetailView(mjonMsgDetailSentVO);
+		
+		
+		// 성공 대기 실패 발송금액 분할여부
+		MjonMsgSentVO updatedVO = getDetailFunction(resultVO.getMsgGroupId(), resultVO.getEachPrice());
+		resultVO.setResultSValue(updatedVO.getResultSValue()); // 성공건수
+		resultVO.setResultWValue(updatedVO.getResultWValue()); // 대기건수
+		resultVO.setResultFValue(updatedVO.getResultFValue()); // 실패건수
+		resultVO.setTotPrice(updatedVO.getTotPrice());
+		resultVO.setDivideYN(updatedVO.getDivideYN());
+		
+
+		// 퍼센트 계산 실행
+		Map<String, String> returnMap = calculatePercentages(resultVO);
+		resultVO.setSuccessPct(returnMap.get("successPct"));
+		resultVO.setWaitingPct(returnMap.get("waitingPct"));
+		resultVO.setFailedPct(returnMap.get("failedPct"));
+		
+		
+		// 진행상태 코드화
+		String statusCode = getStatusCode(MjonMsgSentVO.builder()
+				.reserveCYn(resultVO.getReserveCYn())
+				.reserveYn(resultVO.getReserveYn())
+				.msgGroupCnt(resultVO.getMsgGroupCnt())
+				.resultSValue(resultVO.getResultSValue())
+				.resultWValue(resultVO.getResultWValue())
+				.resultFValue(resultVO.getResultFValue())
+				.diffMin(resultVO.getDiffMin())
+				.build());
+		log.info(" ++ statusCode :: [{}]" ,statusCode);
+		resultVO.setStatusCd(statusCode);
+		
+		// 결과 출력
+		System.out.println("성공률: " + returnMap.get("successPct"));
+		System.out.println("대기율: " + returnMap.get("waitingPct"));
+		System.out.println("실패율: " + returnMap.get("failedPct"));
+		
 		
 		// 광고일떄 (광고)와 줄바꿈+무료거부 0808800858 삭제
-		if("A".equals(result.getMsgKind())) {
-			result.setSmsTxt(result.getSmsTxt().replace("(광고)", "")
+		if("A".equals(resultVO.getMsgKind())) {
+			resultVO.setSmsTxt(resultVO.getSmsTxt().replace("(광고)", "")
 					.replaceAll("\\s*무료거부 0808800858", ""));
 		}
 		
-		if(StringUtils.isNotEmpty(result.getFileCnt()) && Integer.parseInt(result.getFileCnt()) > 0)
+		if(StringUtils.isNotEmpty(resultVO.getFileCnt()) && Integer.parseInt(resultVO.getFileCnt()) > 0)
 		{
 			
-			List<FileInfoVO> fileInfos = getFileInfo(result);
-			result.setFileInfos(fileInfos);
+			List<FileInfoVO> fileInfos = getFileInfo(resultVO);
+			resultVO.setFileInfos(fileInfos);
 		}
 		
 		
 		
 		
-		resultMap.put("result", result);
+		resultMap.put("result", resultVO);
 		
 		return resultMap;
 	}
@@ -428,11 +438,11 @@
 		//예약 관리 리스트 불러오기
 		List<MjonMsgSentVO> resultAllSentList = mjonMsgSentDAO.selectAllMsgSentList_advc(mjonMsgSentVO);
 
-		long startTime = System.nanoTime(); // 시작 시간 측정
+//		long startTime = System.nanoTime(); // 시작 시간 측정
 		resultAllSentList = makeDetailFunction(resultAllSentList);
-		long endTime = System.nanoTime(); // 끝난 시간 측정
-		double executionTimeInSeconds = (endTime - startTime) / 1_000_000_000.0;
-		System.out.println("Execution time: " + executionTimeInSeconds + " seconds");
+//		long endTime = System.nanoTime(); // 끝난 시간 측정
+//		double executionTimeInSeconds = (endTime - startTime) / 1_000_000_000.0;
+//		System.out.println("Execution time: " + executionTimeInSeconds + " seconds");
 
 		SXSSFWorkbook  workbook = null; // SXSSFWorkbook 변수 선언
 		try{ 
@@ -658,41 +668,122 @@
 	}
 	
 
+	/** 
+	 * @methodName	: makeDetailFunction 
+	 * @author		: 이호영
+	 * @date		: 2025.02.04 
+	 * @description	: 발송결과 성공건수, 실패건수, 대기건수, 분할발송여부, 총 발송금액 구하기
+	 * @param resultList
+	 * @return 
+	 */
 	private List<MjonMsgSentVO> makeDetailFunction(List<MjonMsgSentVO> resultList) {
 		resultList.stream().forEach(t->{
-			MjonMsgSWFDTO mjonMsgSWFDTO =	mjonMsgSentDAO.findBySWF(t.getMsgGroupId());
-			t.setResultSValue(String.valueOf(mjonMsgSWFDTO.getResultSValue())); // 성공건수
-			t.setResultFValue(String.valueOf(mjonMsgSWFDTO.getResultFValue())); // 실패건수
-			t.setResultWValue(String.valueOf(mjonMsgSWFDTO.getResultWValue())); // 대기건수
-			t.setDivideYN(mjonMsgSWFDTO.getDivideYN());
-			
-			
-			// TotPrice : 성공건수에 대한 금액 곱하기
-			BigDecimal eachPrice = new BigDecimal(t.getEachPrice());
-			BigDecimal resultSValue = new BigDecimal(mjonMsgSWFDTO.getResultSValue());
-			BigDecimal totalPrice = eachPrice.multiply(resultSValue);
-			// 소수점 한 자리로 설정 (반올림)// totalPrice 값을 소수점 한 자리까지 반올림하여 roundedTotalPrice에 저장
-			// RoundingMode.HALF_UP: 반올림 방식으로, 소수점 기준 5 이상이면 올림, 그렇지 않으면 내림
-			BigDecimal roundedTotalPrice = totalPrice.setScale(1, RoundingMode.HALF_UP);
+			MjonMsgSentVO updatedVO = getDetailFunction(t.getMsgGroupId(), t.getEachPrice());
 
-			// roundedTotalPrice가 0인지 확인
-			// BigDecimal.compareTo(BigDecimal.ZERO)는 값을 비교하는 메서드
-			// 결과:
-			// - 반환 값이 0이면 두 값이 같음
-			// - 반환 값이 음수이면 roundedTotalPrice가 0보다 작음
-			// - 반환 값이 양수이면 roundedTotalPrice가 0보다 큼
-			if (roundedTotalPrice.compareTo(BigDecimal.ZERO) == 0) {
-				// roundedTotalPrice 값이 0이면, "-" 문자열을 totPrice에 설정
-				t.setTotPrice("-"); 
-			} else {
-				// roundedTotalPrice 값이 0이 아닌 경우
-				// 반올림된 BigDecimal 값을 toPlainString()을 사용하여 문자열로 변환 후 totPrice에 설정
-				// toPlainString(): 지수 표기법 없이 일반적인 문자열 형태로 반환
-				t.setTotPrice(roundedTotalPrice.toPlainString());
-			}
+			t.setResultSValue(updatedVO.getResultSValue());
+			t.setResultFValue(updatedVO.getResultFValue());
+			t.setResultWValue(updatedVO.getResultWValue());
+			t.setDivideYN(updatedVO.getDivideYN());
+			t.setTotPrice(updatedVO.getTotPrice());
 		});
 		return resultList;
 	}
 	
 	
+	private MjonMsgSentVO getDetailFunction(String p_msgGroupId, String p_eachPrice) {
+		
+
+		MjonMsgSentVO returnVO = new MjonMsgSentVO(); 
+		
+		MjonMsgSWFDTO mjonMsgSWFDTO =	mjonMsgSentDAO.findBySWF(p_msgGroupId);
+		returnVO.setResultSValue(String.valueOf(mjonMsgSWFDTO.getResultSValue())); // 성공건수
+		returnVO.setResultFValue(String.valueOf(mjonMsgSWFDTO.getResultFValue())); // 실패건수
+		returnVO.setResultWValue(String.valueOf(mjonMsgSWFDTO.getResultWValue())); // 대기건수
+		returnVO.setDivideYN(mjonMsgSWFDTO.getDivideYN());
+		
+		
+		// TotPrice : 성공건수에 대한 금액 곱하기
+		BigDecimal eachPrice = new BigDecimal(p_eachPrice);
+		BigDecimal resultSValue = new BigDecimal(mjonMsgSWFDTO.getResultSValue());
+		BigDecimal totalPrice = eachPrice.multiply(resultSValue);
+		// 소수점 한 자리로 설정 (반올림)// totalPrice 값을 소수점 한 자리까지 반올림하여 roundedTotalPrice에 저장
+		// RoundingMode.HALF_UP: 반올림 방식으로, 소수점 기준 5 이상이면 올림, 그렇지 않으면 내림
+		BigDecimal roundedTotalPrice = totalPrice.setScale(1, RoundingMode.HALF_UP);
+
+		// roundedTotalPrice가 0인지 확인
+		// BigDecimal.compareTo(BigDecimal.ZERO)는 값을 비교하는 메서드
+		// 결과:
+		// - 반환 값이 0이면 두 값이 같음
+		// - 반환 값이 음수이면 roundedTotalPrice가 0보다 작음
+		// - 반환 값이 양수이면 roundedTotalPrice가 0보다 큼
+		if (roundedTotalPrice.compareTo(BigDecimal.ZERO) == 0) {
+			// roundedTotalPrice 값이 0이면, "-" 문자열을 totPrice에 설정
+			returnVO.setTotPrice("-"); 
+		} else {
+			// roundedTotalPrice 값이 0이 아닌 경우
+			// 반올림된 BigDecimal 값을 toPlainString()을 사용하여 문자열로 변환 후 totPrice에 설정
+			// toPlainString(): 지수 표기법 없이 일반적인 문자열 형태로 반환
+			returnVO.setTotPrice(roundedTotalPrice.toPlainString());
+		}
+		log.info(" + returnVO.getTotPrice() :: [{}]", returnVO.getTotPrice());
+		return returnVO;
+		
+		
+	}
+	
+
+	
+	private Map<String, String> calculatePercentages(MjonMsgDetailSentVO result) {
+		int total = Integer.parseInt(result.getMsgGroupCnt()); // 전체 건수
+		int success = Integer.parseInt(result.getResultSValue()); // 성공 건수
+		int waiting = Integer.parseInt(result.getResultWValue()); // 대기 건수
+		int failed = Integer.parseInt(result.getResultFValue()); // 실패 건수
+
+		// 퍼센트 계산 (0으로 나누는 경우 대비)
+		String successPct = total > 0 ? String.format("%.1f%%", (success / (double) total) * 100) : "0.0%";
+		String waitingPct = total > 0 ? String.format("%.1f%%", (waiting / (double) total) * 100) : "0.0%";
+		String failedPct = total > 0 ? String.format("%.1f%%", (failed / (double) total) * 100) : "0.0%";
+
+		// 결과 맵에 저장
+		Map<String, String> percentages = new HashMap<>();
+		percentages.put("successPct", successPct);
+		percentages.put("waitingPct", waitingPct);
+		percentages.put("failedPct", failedPct);
+
+		return percentages;
+		
+	}
+	
+	// 공통코드 ITN057에 대한 코드화 진행
+	/*
+	 * CODE_ID	CODE	CODE_NM		CODE_DC
+	 * ITN057	01		진행중		진행중
+	 * ITN057	02		완료			완료출해야함
+	 * ITN057	03		예약대기		예약대기(발송전) 버튼으로 노출해야함
+	 * ITN057	04		-			예약취소 ( - 으로 노출 )
+	 * */
+	private String getStatusCode(MjonMsgSentVO result) {
+
+		String returnCode;
+		
+		if ("Y".equals(result.getReserveCYn())) {
+			returnCode = "04"; // 예약취소 코드
+		} else if (
+				"Y".equals(result.getReserveYn()) 
+					&& "N".equals(result.getReserveCYn()) 
+					&& result.getMsgGroupCnt().equals(result.getResultWValue())
+					&& result.getDiffMin() < -5 // 예약 시간이 5분 이상인 것들만
+					) {
+			returnCode = "03"; // 예약대기 코드 ( 예약취소 버튼 노출 )
+		} else if (result.getMsgGroupCnt().equals(result.getResultSValue()) 
+					|| result.getMsgGroupCnt().equals(result.getResultFValue())) {
+			returnCode = "02"; // 완료 코드
+		} else {
+			returnCode = "01"; // 진행중 코드
+		}
+		
+		
+		return returnCode;
+		
+	}
 }
src/main/java/itn/let/mjo/msgsent/web/MjonMsgSentController.java
--- src/main/java/itn/let/mjo/msgsent/web/MjonMsgSentController.java
+++ src/main/java/itn/let/mjo/msgsent/web/MjonMsgSentController.java
@@ -164,13 +164,21 @@
     /**
      * 발송관리 화면 
      * @param searchVO
-     * @param model
+     * @param model/web/user/login/login.do
      * @return	"/web/mjon/msgtxt/selectMsgTxtView.do"
      * @throws Exception
      */
 	@RequestMapping(value= {"/web/mjon/msgsent/msgSentDetailView.do"})
-	public String selectMsgSentDetailView(@ModelAttribute("searchVO") MjonMsgDetailSentVO mjonMsgDetailSentVO,
-			RedirectAttributes redirectAttributes,	ModelMap model) throws Exception{
+	public String selectMsgSentDetailView(@ModelAttribute("searchVO") MjonMsgDetailSentVO mjonMsgDetailSentVO
+			, ModelMap model) throws Exception{
+		
+
+			//로그인 권한정보 불러오기
+			LoginVO loginVO = EgovUserDetailsHelper.isAuthenticated()? (LoginVO)EgovUserDetailsHelper.getAuthenticatedUser():null;
+			String userId = loginVO == null ? "" : EgovStringUtil.isNullToString(loginVO.getId());
+			if(loginVO == null) {
+				return "redirect:/web/user/login/login.do";
+			}
 		
 			Map<String, Object> resultMap = mjonMsgSentService.selectAllMsgSentDetailView(mjonMsgDetailSentVO);
 			model.addAttribute("result", resultMap.get("result"));
src/main/resources/egovframework/sqlmap/let/msg/MjonMsgSent_SQL_mysql.xml
--- src/main/resources/egovframework/sqlmap/let/msg/MjonMsgSent_SQL_mysql.xml
+++ src/main/resources/egovframework/sqlmap/let/msg/MjonMsgSent_SQL_mysql.xml
@@ -354,18 +354,22 @@
 			MGD.MSG_GROUP_ID as msgGroupId
 			, MGD.MSG_GROUP_CNT as msgGroupCnt 
 			, MGD.RESERVE_YN as reserveYn
+			, MGD.RESERVE_C_YN as reserveCYn
 			, MGD.CALL_FROM  as callFrom 
 			, MGD.USER_ID as userId
 			, MGD.SMS_TXT as smsTxt
 			, MGD.SUBJECT as subject
-			, MGD.REQ_DATE as reqdate
+			, DATE_FORMAT(MGD.REQ_DATE, '%Y-%m-%d %H:%i') as reqDate 
+			, DATE_FORMAT(MGD.REGDATE, '%Y-%m-%d %H:%i') as regDate
 			, MGD.MSG_TYPE as msgType
 			, MGD.MSG_KIND as msgKind
-			, MD.SENT_DATE as sentDate
+			, MGD.EACH_PRICE  as eachPrice
+			, DATE_FORMAT(MD.SENT_DATE, '%Y-%m-%d %H:%i') as sentDate
 			, MD.FILE_CNT  as fileCnt
 			, MD.FILE_PATH1 as filePath1
 			, MD.FILE_PATH2 as filePath2
 			, MD.FILE_PATH3 as filePath3 
+			, TIMESTAMPDIFF(minute, DATE_FORMAT(MGD.REQ_DATE, '%Y-%m-%d %T'), DATE_FORMAT(NOW(), '%Y-%m-%d %T')) as diffMin
 		from
 			MJ_MSG_GROUP_DATA MGD
 		inner join MJ_MSG_DATA MD on
src/main/webapp/WEB-INF/jsp/web/msgsent/MsgSentDetailView.jsp
--- src/main/webapp/WEB-INF/jsp/web/msgsent/MsgSentDetailView.jsp
+++ src/main/webapp/WEB-INF/jsp/web/msgsent/MsgSentDetailView.jsp
@@ -3,8 +3,24 @@
 <%@ taglib prefix="ui" uri="http://egovframework.gov/ctl/ui"%>
 <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
 <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
+<%@ taglib prefix="ec" uri="/WEB-INF/tld/ecnet_tld.tld"%>
 <%@ page import="itn.com.cmm.LoginVO" %>
 
+
+
+<style>
+/* Tabulator Placeholder 기본 스타일 유지 */
+.tabulator-placeholder {
+	font-size: 22px !important; /* 기존 폰트 크기 유지 */
+	color: #e2d6d6 !important; /* 기존 색상 유지 */
+	font-weight: normal !important; /* 기본 폰트 두께 유지 */
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	height: 100%;
+}
+
+</style>
 
 <script type="text/javascript">
 
@@ -16,36 +32,31 @@
 	$tbDtailList = new Tabulator("#detailPopup", {
 		height: "255px",
 		width: "20%",
-		layout: "fitDataStretch", // 데이터가 너비에 맞게 늘어나도록 설정
+		// layout: "fitDataStretch", // 데이터가 너비에 맞게 늘어나도록 설정
+		layout: "fitColumns", // fitDataStretch 대신 fitColumns 사용
 		autoColumns: false,
 		headerHozAlign: "center",
 		validationMode: "highlight",
 		clipboard: false,
 		clipboardCopySelector: "table",
 		clipboardPasteAction: "insert", // insert, update, replace
-        columns: [
-            {
-                title: "이름",
-                field: "addrNm",
-                hozAlign: "center",
-                headerHozAlign: "center",
-                width: 140
-            },
-            {
-                title: "휴대폰",
-                field: "phone",
-                hozAlign: "center",
-                headerHozAlign: "center",
-                width: 200
-            },
-            {
-                title: "상세결과",
-                field: "result",
-                hozAlign: "center",
-                headerHozAlign: "center",
-                width: 180
-            }
-        ]
+		placeholder:"데이터를 불러오고 있습니다...", 
+		columns: [
+			{
+				title: "휴대폰",
+				field: "phone",
+				hozAlign: "center",
+				headerHozAlign: "center",
+				widthGrow: 1
+			},
+			{
+				title: "상세결과",
+				field: "result",
+				hozAlign: "center",
+				headerHozAlign: "center",
+				widthGrow: 1
+			}
+		]
 	});
 	
 	fn_getDetailList();
@@ -56,11 +67,42 @@
 		$("#goList").submit();
 	});
 	
+
+	// 검색 버튼 클릭 시 실행
+	$("#searchBtn").on("click", function () {
+		searchTable();
+	});
+
+	// Enter 키 입력 시 실행
+	$("#searchInput").on("keyup", function (event) {
+		if (event.key === "Enter") {
+			searchTable();
+		}
+	});
 	
 	
 	
-	
-	
+	// 탭 버튼 클릭 이벤트
+	$(".tabType3 .tab button").on("click", function () {
+		console.log('.tabType3 .tab button');
+		// 모든 탭의 active 클래스 제거
+		$(".tabType3 .tab").removeClass("active");
+
+		// 클릭한 버튼의 부모 요소(li)에 active 클래스 추가
+		$(this).parent().addClass("active");
+		
+		// 기존 버튼들의 title 속성 초기화
+		$(".tabType3 .tab button").removeAttr("title");
+
+		// 선택된 버튼의 title 속성을 "선택됨"으로 변경
+		$(this).attr("title", "선택됨");
+		
+
+		// 클릭한 버튼의 텍스트 가져오기
+		let resultText = $(this).text().trim();
+		fn_tabTable('result', resultText);
+		
+	});
 	
 });
 
@@ -91,11 +133,12 @@
 }
 
 
+/** 
+ * @Description: 데이터 설정
+ */
 function fn_setData(data){
-	console.log('data : ', data);
-	
-
-	// $tbDtailList.clearData();
+// 	console.log('data : ', data);
+// 	$tbDtailList.clearData();
 
 	const resultData = [];  // 오류 데이터를 저장할 배열
 
@@ -115,7 +158,70 @@
 	
 };
 
+/**
+ * @Description: 검색 기능
+ */
+function searchTable() {
+	let column = $("#searchColumn").val(); // 선택한 컬럼
+	let keyword = $("#searchInput").val(); // 검색어
 
+	// 검색어가 3자 이상일 때만 필터 적용
+// 	if (keyword.length >= 3) {
+	$tbDtailList.setFilter(column, "like", keyword);
+	fn_setPlaceholder("검색 결과가 없습니다.");
+// 	} else {
+// 		$tbDtailList.clearFilter(); // 검색어가 짧으면 필터 제거
+// 	}
+}
+
+/**
+ * @Description: 탭 검색 기능
+ */
+function fn_tabTable(column, keyword) {
+
+	if(keyword == '전체'){
+		$tbDtailList.clearFilter();
+	}else{
+		$tbDtailList.setFilter(column, "like", keyword);
+	}
+	
+	fn_setPlaceholder("검색 결과가 없습니다.");
+}
+
+/**
+ * @Description: 타블레이서 설명 수정 
+ */
+function fn_setPlaceholder(msg){
+
+	// 검색 후 데이터가 있는지 확인 후 placeholder 변경
+	setTimeout(() => {
+		let filteredRows = $tbDtailList.getRows('active').length; // 필터링된 행 개수 가져오기
+		if (filteredRows === 0) {
+			// 기존 데이터 유지하면서 빈 데이터 추가하여 placeholder 변경
+			$(".tabulator-placeholder").text(msg); // placeholder 메시지 변경
+		} 
+	}, 300); // 필터 적용 후 반영되도록 약간의 딜레이 추가
+}
+
+
+/**
+ * @Description: 필터링된 데이터만 다운로드
+ */
+function fn_downloadFilteredExcel() {
+	let filteredData = $tbDtailList.getData("active"); // 필터링된 데이터 가져오기
+	
+	if (filteredData.length === 0) {
+		alert("다운로드할 데이터가 없습니다.");
+		return;
+	}
+
+//     console.log("엑셀 다운로드 - 필터링된 데이터:", filteredData);
+
+	$tbDtailList.download("xlsx", "filtered_data.xlsx", {
+		sheetName: "Filtered Data",
+		data: filteredData // 필터링된 데이터만 다운로드
+	});
+}
 
 </script>
 <div class="inner">
@@ -144,95 +250,136 @@
 			<div class="send_general">
 				<!-- 발송결과 상세 정보 -->
 				<div class="resultcont_left">
-					<!--발송정보-->
-					<div class="res_info">
-						<div class="res_info_in">
-							<div class="res_info_top clearfix">
-								<p>발송정보</p>
-								<p><button type="button" class="btnType btnType3">재전송</button></p>
+							<!--발송정보-->
+							<div class="res_info">
+								<div class="res_info_in">
+									<div class="res_info_top clearfix">
+										<p>발송정보</p>
+										<p><button type="button" class="btnType btnType3">재전송</button></p>
+									</div>
+									<div class="res_info_btm">
+										<dl>
+											<dt>발송일시</dt>
+											<dd>
+												${result.reqDate}
+											</dd>
+										</dl>
+										<c:if test="${result.reserveYn eq 'Y' }">
+										<dl><!-- 예약 시 -->
+											<dt>등록일시</dt>
+											<dd>${result.regDate }</dd>
+										</dl>
+										</c:if>
+										<dl>
+											<dt>제목</dt>
+											<dd>${result.subject }</dd>
+										</dl>
+										<dl>
+											<dt>형태</dt>
+											<dd>
+												${result.msgType == '4' 
+														? '단문' 
+														: (result.fileCnt == '0' 
+																	? '장문' 
+																	: '그림')}
+											</dd>
+										</dl>
+										<dl>
+											<dt>발송건수</dt>
+											<dd><span class="c_222"><fmt:formatNumber value="${result.msgGroupCnt}" type="number" groupingUsed="true" /></span>건</dd>
+										</dl>
+										<dl>
+											<dt>발신번호</dt>
+											<dd>${result.callFrom }</dd>
+										</dl>
+										<dl>
+											<dt>진행상황
+											<!--  예약인 경우 --> 
+											<c:if test="${result.reserveYn eq 'Y'}">
+													<div class="icon_wrap">
+														<div class="re">예약</div>
+													<!-- 예약일때만 분할이 있음 -->
+													<c:if test="${result.divideYN eq 'Y'}">
+														<div class="di_info">
+															<button class="di">분할</button>
+															<div class="di_hover_layer">
+																<strong>100,000건</strong>씩 <strong>35분</strong> 간격
+															</div>
+														</div>
+													</c:if>
+													</div>
+											</c:if>
+											<!--//  예약인 경우 --> 
+											</dt>
+											<!--<dd>진행중</dd>-->
+											<!--<dd>완료</dd>-->
+											<dd>
+											
+											<c:choose>
+												<c:when test="${result.statusCd ne '03' }">
+													<ec:code codeId="ITN057" code="${result.statusCd }" />
+												</c:when>
+												<c:otherwise>
+													<p><button class="btnType btnType20" onClick="javascript:fnReservCancel('${result.msgGroupId}'); return false;">예약취소</button></p>
+												</c:otherwise>
+											</c:choose>
+							
+								<!--  -->
+<!-- 												<button class="btnType btnType20">예약취소</button> -->
+											</dd>
+											<!--<dd>예약취소 2024-07-16 15:07</dd>--><!-- 예역취소 후 버튼 대신 취소 일시 노출 -->											
+										</dl>
+									</div>
+									<div class="res_info_btm">
+										<dl>
+											<dt class="btm_charge">발송요금</dt>
+											<dd><span class="stcharge">78,100</span>원</dd>											
+											<!--<dd><span class="stcharge">-</span>원</dd>--><!-- 예역취소 후 금액은 하이픈 처리--> 
+										</dl>
+									</div>
+								</div>                                
 							</div>
-							<div class="res_info_btm">
-								<dl>
-									<dt>발송일시</dt>
-									<dd>2024-07-18 15:25</dd>
-								</dl>
-								<dl>
-									<dt>형태</dt>
-									<dd>그림</dd>
-								</dl>
-								<dl>
-									<dt>발송건수</dt>
-									<dd><span class="c_222">100</span>건</dd>
-								</dl>
-								<dl>
-									<dt>발신번호</dt>
-									<dd>010-1234-5678</dd>
-								</dl>
-								<dl>
-									<dt>예약관리</dt>
-									<!--<dd>-</dd>--><!-- 예약건이 아닌 경우는 하이픈 처리-->
-									<dd><button class="btnType btnType25">예약취소</button></dd>
-									<!--<dd>예약취소 2024-07-16 15:07</dd>--><!-- 예역취소 후 버튼 대신 취소 일시 노출 -->
-								</dl>
+							 <!--// 발송정보-->
+							<!--상세결과-->
+							<div class="res_info">
+								<div class="res_info_in">
+									<div class="res_info_top clearfix" style="padding:0 0 10px 0;">
+										<p>상세결과</p>
+										<p></p>
+									</div>
+									<div class="res_num">
+										<div class="res_info_btm1">
+											<dl>
+												<dt>전체건수</dt>
+												<dd><a href="#" data-tooltip="rev_popup04"><span class="c_222_g"><fmt:formatNumber value="${result.msgGroupCnt}" type="number" groupingUsed="true" /></span>건</a></dd>
+											</dl>
+										</div>
+										<div class="res_info_btm1">
+											<dl>
+												<dt>성공건수</dt>
+												<dd><span class="c_002c9a_g"><fmt:formatNumber value="${result.resultSValue}" type="number" groupingUsed="true" /></span>건(${result.successPct})</dd>
+											</dl>
+										</div>
+									</div>
+									<div class="res_num">	
+										<div class="res_info_btm1">
+											<dl>
+												<dt>대기건수</dt>
+												<dd><span class="c_666_g"><fmt:formatNumber value="${result.resultWValue}" type="number" groupingUsed="true" /></span>건(${result.waitingPct})</dd>
+											</dl>
+										</div>
+										<div class="res_info_btm1">
+											<dl>
+												<dt>실패건수</dt>
+												<dd><span class="c_e40000_g"><fmt:formatNumber value="${result.resultFValue}" type="number" groupingUsed="true" /></span>건(${result.failedPct})</dd>
+											</dl>
+										</div>
+									</div>
+									<p class="table_bottom_txt">* 전체건수를 클릭하면 받는 사람 상세정보를 확인하실 수 있습니다.</p>
+								</div>                                
 							</div>
-							<div class="res_info_btm">
-								<dl>
-									<dt class="btm_charge">발송요금</dt>
-									<dd><span class="stcharge">100</span>원</dd>											
-									<!--<dd><span class="stcharge">-</span>원</dd>--><!-- 예역취소 후 금액은 하이픈 처리--> 
-								</dl>
-							</div>
-						</div>                                
-					</div>
-					 <!--// 발송정보-->
-					<!--상세결과-->
-					<div class="res_info">
-						<div class="res_info_in">
-							<div class="res_info_top clearfix" style="padding:0 0 10px 0;">
-								<p>상세결과</p>
-								<p><button type="button" class="refresh_btn btnType"><i class="refresh_img"></i>새로고침</button></p>
-							</div>
-							<div class="res_num">
-								<div class="res_info_btm1">
-									<dl>
-										<dt>전체건수</dt>
-										<dd><a href="#" data-tooltip="rev_popup04"><span class="c_222_g">101</span>건(100%)</a></dd>
-									</dl>
-								</div>
-								<div class="res_info_btm1">
-									<dl>
-										<dt>성공건수</dt>
-										<dd><span class="c_002c9a_g">100</span>건(100%)</dd>
-									</dl>
-								</div>
-							</div>
-							<div class="res_num">	
-								<div class="res_info_btm1">
-									<dl>
-										<dt>대기건수</dt>
-										<dd><span class="c_666_g">0</span>건(0%)</dd>
-									</dl>
-								</div>
-								<div class="res_info_btm1">
-									<dl>
-										<dt>실패건수</dt>
-										<dd><span class="c_e40000_g">1</span>건(100%)</dd>
-									</dl>
-								</div>
-							</div>
-						</div>                                
-					</div>
-					 <!--// 발송결과-->	
+							 <!--// 발송결과-->	
 
-					 <div class="table_btn clearfix">
-						<div class="table_btn_left">
-							<button type="button" data-tooltip="rev_popup02" class="btnType btnType14"><i class="add_img"></i>주소록 등록</button>
-							<button type="button" class="excel_btn btnType"><i class="downroad"></i>엑셀 다운로드</button>
-						</div>
-						<div class="table_btn_right">
-							<p class="table_btn_right_txt">* 전체 건수를 클릭하면 받는 사람 상세정보를 확인하실 수 있습니다.</p>
-						</div>
-					</div>						 
 				</div>
 				<!--// 발송결과 상세 정보 -->
 
@@ -294,12 +441,12 @@
 									</div>
 									<div class="preview_auto">
 										<c:if test="${result.msgKind eq 'A' }" >
-										<p class="ad_tit">(광고)</p>
+											<p class="ad_tit">(광고)</p>
 										</c:if>
 										<p class="none_txt"><c:out value="${result.smsTxt }" /></p>
 										<p class="realtime"></p>
 										<c:if test="${result.msgKind eq 'A' }" >
-										<p class="deny_receipt">무료 거부 080-0000-0000</p>
+											<p class="deny_receipt">무료 거부 080-0000-0000</p>
 										</c:if>
 									</div>
 								</div>
@@ -327,7 +474,7 @@
 
 	<!-- 발송대상리스트 팝업 -->
 	<div class="tooltip-wrap">
-		<div class="popup-com ad_layer rev_popup04" tabindex="0" data-tooltip-con="rev_popup04" data-focus="rev_popup04" data-focus-prev="rev_popup04-close" style="width:595px;">
+		<div class="popup-com ad_layer rev_popup04" tabindex="0" data-tooltip-con="rev_popup04" data-focus="rev_popup04" data-focus-prev="rev_popup04-close" style="width:530px;">
 			<div class="popup_heading">
 				<p>발송대상 리스트</p>
 				<button type="button" class="tooltip-close" data-focus="rev_popup04-close"><img src="/publish/images/content/layerPopup_close.png" alt="팝업 닫기"></button>
@@ -343,113 +490,35 @@
 
 				<div class="popup_search_type1">								
 					<label for="" class="label">검색종류 선택</label>
-					<select id="" class="selType2 select_btn">
-						<option value="1">수신번호</option>
-						<option value="2">이름</option>
-						<option value="3">상세결과</option>
+					<select id="searchColumn" class="selType1 select_btn">
+						<option value="phone">수신번호</option>
+						<option value="result">상세결과</option>
 					</select>
 					<label for="" class="label">검색어입력</label>
-					<input type="text" class="send_text" id="" name="" value="" placeholder="3자 이상 입력하세요." onfocus="this.placeholder=''" onblur="this.placeholder='3자 이상 입력하세요.'">
-					<button type="button" class="btnType btnType2" style="width:63px; margin:0;">검색</button>
+					<input type="text" class="send_text" id="searchInput" name="" value="" placeholder="3자 이상 입력하세요." onfocus="this.placeholder=''" onblur="this.placeholder='3자 이상 입력하세요.'">
+					<button type="button" id="searchBtn" class="btnType btnType2" style="width:63px; margin:0;">검색</button>
 				</div>
 
+				<div class="list_tab_wrap2 type4">
+					<ul class="tabType3" id="tabType" name="tabType">
+						<li class="tab active"><button type="button" title="선택됨">전체</button></li>
+						<li class="tab"><button type="button">대기</button></li>
+						<li class="tab"><button type="button">성공</button></li>
+						<li class="tab"><button type="button">실패</button></li>
+					</ul>
+				</div>	
+				
 				<div class="tb_wrap" id="detailPopup" style="min-height:200px;">
-					<%-- <table class="tType4">
-						<colgroup>
-							<col style="width: 30%;">
-							<col style="width: 40%;">
-							<col style="width: 30%;">
-						</colgroup>
-						<thead>
-							<tr>
-								<th>이름
-									<div class="sort_wrap">
-										<input type="button" class="sort sortBtn">
-									</div>
-								</th>
-								<th>수신번호
-									<div class="sort_wrap">
-										<input type="button" class="sort sortBtn">
-									</div>
-								</th>
-								<th>상세결과
-									<div class="sort_wrap">
-										<input type="button" class="sort sortBtn">
-									</div>
-								</th>
-							</tr>
-						</thead>
-						<tbody>
-							<tr>
-								<td>홍길동</td>
-								<td>010-1234-5678</td>
-								<td>성공</td>
-							</tr>
-							<tr>
-								<td>홍길동</td>
-								<td>010-1234-5678</td>
-								<td>성공</td>
-							</tr>
-							<tr>
-								<td>홍길동</td>
-								<td>010-1234-5678</td>
-								<td>성공</td>
-							</tr>
-							<tr>
-								<td>홍길동</td>
-								<td>010-1234-5678</td>
-								<td>성공</td>
-							</tr>
-							<tr>
-								<td>홍길동</td>
-								<td>010-1234-5678</td>
-								<td>성공</td>
-							</tr>
-							<tr>
-								<td>홍길동</td>
-								<td>010-1234-5678</td>
-								<td>성공</td>
-							</tr>
-							<tr>
-								<td>홍길동</td>
-								<td>010-1234-5678</td>
-								<td>성공</td>
-							</tr>
-						</tbody>
-					</table>
- --%>
+						<!-- 타블레이터 영역 -->
 				</div>	
 				
 				<div class="table_btn clearfix">
-					<div class="table_btn_left"></div>
-					<div class="table_btn_right">
+					<div class="table_btn_left">
+						<button type="button" onclick="fn_downloadFilteredExcel()" class="excel_btn btnType"><i class="downroad"></i>엑셀 다운로드</button>
 						<button type="button" data-tooltip="rev_popup02" class="btnType btnType14"><i class="add_img"></i>주소록 등록</button>
-					<button type="button" class="excel_btn btnType"><i class="downroad"></i>엑셀 다운로드</button>
+						<button type="button" class="btnType btnType15"><i class="remove_img"></i>주소록에서 번호 삭제</button>
 					</div>
 				</div>
-				<!-- 
-				pagination
-				<ul class="pagination">
-					<li class="page_first"><button><img src="/publish/images/content/page_first.png"
-								alt=""></button></li>
-					<li class="page_prev"><button><img src="/publish/images/content/page_prev.png"
-								alt=""></button></li>
-					<li class="on"><button>1</button></li>
-					<li><button>2</button></li>
-					<li><button>3</button></li>
-					<li><button>4</button></li>
-					<li><button>5</button></li>
-					<li><button>6</button></li>
-					<li><button>7</button></li>
-					<li><button>8</button></li>
-					<li><button>9</button></li>
-					<li><button>10</button></li>
-					<li class="page_next"><button><img src="/publish/images/content/page_next.png"
-								alt=""></button></li>
-					<li class="page_last"><button><img src="/publish/images/content/page_last.png"
-								alt=""></button></li>
-				</ul>pagination
-				 -->
 			</div>
 			<div class="popup_btn_wrap2" style="margin: -40px auto 30px auto;">
 				<button type="button" class="tooltip-close" data-focus="adr_popup01-close"  data-focus-next="popup02">닫기</button>                      
Add a comment
List