////////////////////////////////////////////////////////////////////////////////////////
//Document Ready
////////////////////////////////////////////////////////////////////////////////////////
const PPT_ORIGINAL_WIDTH = 1280;
const PPT_ORIGINAL_HEIGHT = 720;
let selectedPdServiceId = null; // 제품(서비스) 아이디
let selectedPdServiceTitle = null; // 제품(서비스) 이름
let selectedPdServiceVersionIds = []; // 선택된 버전 아이디
let selectedAssigneeId; // 선택된 작업자 계정 ID
let selectedAssigneeName; // 선택된 작업자 이름
let selectedReportType = "personal"; // 선택된 보고서 유형 ("weekly" | "personal")
let selectedValue = null; // 선택된 주차 값 (예: "2026-W01")
let startDate = null; // 선택된 주차 시작일
let endDate = null; // 선택된 주차 종료일
let versionListData = []; // 버전 목록 데이터
var previousBlobUrl = null;
var previousAudioBlobUrl = null;
var previousAudioBlob = null;
let aiServiceStatus = null; // AI 서비스 상태
const PDF_VIEWER_BASE_URL = "../reference/jquery-plugins/pdfjs-3.11.174-dist/web/viewer.html";
let timeData = [];
let scopeData = [];
let resourceData = [];
function execDocReady() {
var pluginGroups = [
[
"../reference/light-blue/lib/vendor/jquery.ui.widget.js",
"../reference/light-blue/lib/vendor/http_blueimp.github.io_JavaScript-Templates_js_tmpl.js",
"../reference/light-blue/lib/vendor/http_blueimp.github.io_JavaScript-Load-Image_js_load-image.js",
"../reference/light-blue/lib/vendor/http_blueimp.github.io_JavaScript-Canvas-to-Blob_js_canvas-to-blob.js",
"../reference/light-blue/lib/jquery.iframe-transport.js",
"../reference/light-blue/lib/jquery.fileupload.js",
"../reference/light-blue/lib/jquery.fileupload-fp.js",
"../reference/light-blue/lib/jquery.fileupload-ui.js"
],
[
"../reference/jquery-plugins/select2-4.0.2/dist/css/select2_lightblue4.css",
"../reference/jquery-plugins/lou-multi-select-0.9.12/css/multiselect-lightblue4.css",
"../reference/jquery-plugins/multiple-select-1.5.2/dist/multiple-select-bluelight.css",
"../reference/jquery-plugins/select2-4.0.2/dist/js/select2.min.js",
"../reference/jquery-plugins/lou-multi-select-0.9.12/js/jquery.quicksearch.js",
"../reference/jquery-plugins/lou-multi-select-0.9.12/js/jquery.multi-select.js",
"../reference/jquery-plugins/multiple-select-1.5.2/dist/multiple-select.min.js"
],
[
"../reference/jquery-plugins/datetimepicker-2.5.20/build/jquery.datetimepicker.min.css",
"../reference/light-blue/lib/bootstrap-datepicker.js",
"../reference/jquery-plugins/datetimepicker-2.5.20/build/jquery.datetimepicker.full.min.js",
"../reference/lightblue4/docs/lib/widgster/widgster.js",
//d3 변경
"../reference/jquery-plugins/d3-6.7.0/d3.min.js",
"../reference/lightblue4/docs/lib/slimScroll/jquery.slimscroll.min.js",
],
[
"../reference/jquery-plugins/dataTables-1.10.16/media/css/jquery.dataTables_lightblue4.css",
"../reference/jquery-plugins/dataTables-1.10.16/extensions/Responsive/css/responsive.dataTables_lightblue4.css",
"../reference/jquery-plugins/dataTables-1.10.16/extensions/Select/css/select.dataTables_lightblue4.css",
"../reference/jquery-plugins/dataTables-1.10.16/media/js/jquery.dataTables.min.js",
"../reference/jquery-plugins/dataTables-1.10.16/extensions/Responsive/js/dataTables.responsive.min.js",
"../reference/jquery-plugins/dataTables-1.10.16/extensions/Select/js/dataTables.select.min.js",
"../reference/jquery-plugins/dataTables-1.10.16/extensions/RowGroup/js/dataTables.rowsGroup.min.js",
"../reference/jquery-plugins/dataTables-1.10.16/extensions/Buttons/js/dataTables.buttons.min.js",
"../reference/jquery-plugins/dataTables-1.10.16/extensions/Buttons/js/buttons.html5.js",
"../reference/jquery-plugins/dataTables-1.10.16/extensions/Buttons/js/buttons.print.js",
"../reference/jquery-plugins/dataTables-1.10.16/extensions/Buttons/js/jszip.min.js",
"../reference/jquery-plugins/swiper-11.1.4/swiper-bundle.min.js",
"../reference/jquery-plugins/swiper-11.1.4/swiper-bundle.min.css",
"./js/common/swiperHelper.js",
"./css/customSwiper.css",
// echarts
"../reference/jquery-plugins/echarts-5.5.0/dist/echarts.min.js",
"./js/common/chart/eCharts/basicRadar.js",
// PDF.js 3.x (ES Module 미사용 - Nginx MIME type 문제 해결)
// viewer.html에서 pdf.js, viewer.js를 직접 로드하므로 여기서는 로드 불필요
"../reference/jquery-plugins/PPTXjs-1.21.1/PPTXjs-1.21.1/js/jszip.min.js",
"../reference/jquery-plugins/PPTXjs-1.21.1/PPTXjs-1.21.1/js/pptxjs.min.js",
"../reference/jquery-plugins/PPTXjs-1.21.1/PPTXjs-1.21.1/css/pptxjs.css",
"./js/reportWeekly/reportWeeklyApi.js"
]
// 추가적인 플러그인 그룹들을 이곳에 추가하면 됩니다.
];
loadPluginGroupsParallelAndSequential(pluginGroups)
.then(function () {
console.log("모든 플러그인 로드 완료");
// DWR targetPage 등록 (현재 페이지 등록)
Chat.setCurrentPage("report-performance");
//사이드 메뉴 처리
$(".widget").widgster();
setSideMenu("sidebar_menu_insight", "sidebar_menu_report", "sidebar_menu_report_performance");
//제품(서비스) 셀렉트 박스 이니시에이터
makePdServiceSelectBox();
//버전 멀티 셀렉트 박스 이니시에이터
makeVersionMultiSelectBox();
// 작업자 목록 멀티 셀렉트 박스 이니시에이터
makeAssigneeSelectBox();
//주차 선택 셀렉트 박스 이니시에이터 (제품/버전 선택 후 동적 생성)
initWeekSelectBox();
// 보고서 유형 선택 토글 이벤트
initReportTypeToggle();
// AI 서비스 상태 확인
checkAiServiceStatus();
//PPT 섹션 반응형 스케일링 초기화
initPptScaling();
//PDF Viewer 초기화 - 테스트용 PDF 로드
initPdfViewer();
downloadPdfViewer();
})
.catch(function () {
console.error("플러그인 로드 중 오류 발생");
});
}
////////////////////////////////////////////////////////////////////////////////////////
//제품 서비스 셀렉트 박스
////////////////////////////////////////////////////////////////////////////////////////
function makePdServiceSelectBox() {
//제품 서비스 셀렉트 박스 이니시에이터
$("#selected_pdService").each(function () {
console.log("[ fullDataSheet :: makePdServiceSelectBox ] :: select2 data => ", $(this).data());
$(this).select2($(this).data());
});
//제품 서비스 셀렉트 박스 데이터 바인딩
$.ajax({
url: "/auth-user/api/arms/pdServicePure/getPdServiceMonitor.do",
type: "GET",
contentType: "application/json;charset=UTF-8",
dataType: "json",
progress: true,
statusCode: {
200: function (data) {
//////////////////////////////////////////////////////////
pdServiceListData = [];
for (var k in data.response) {
var obj = data.response[k];
pdServiceListData.push({ pdServiceId: obj.c_id, pdServiceName: obj.c_title });
var newOption = new Option(obj.c_title, obj.c_id, false, false);
$("#selected_pdService").append(newOption).trigger("change");
}
//////////////////////////////////////////////////////////
jSuccess("제품(서비스) 조회가 완료 되었습니다.");
}
},
error: function (e) {
jError("제품(서비스) 조회 중 에러가 발생했습니다.");
}
});
$("#selected_pdService").on("select2:open", function () {
//슬림스크롤
makeSlimScroll(".select2-results__options");
});
// --- select2 ( 제품(서비스) 검색 및 선택 ) 이벤트 --- //
$("#selected_pdService").on("select2:select", function (e) {
selectedPdServiceId = $("#selected_pdService").val();
let pdServiceTitle = $("#select2-selected_pdService-container").attr("title");
selectedPdServiceTitle = pdServiceTitle;
// 제품( 서비스 ) 선택했으니까 자동으로 버전을 선택할 수 있게 유도
// 디폴트는 base version 을 선택하게 하고 ( select all )
//~> 이벤트 연계 함수 :: Version 표시 jsTree 빌드
bind_VersionData_By_PdService();
// 제품 변경 시 플레이어 초기화
resetAudioPlayer();
});
} // end makePdServiceSelectBox()
////////////////////////////////////////////////////////////////////////////////////////
//버전 멀티 셀렉트 박스
////////////////////////////////////////////////////////////////////////////////////////
function makeVersionMultiSelectBox() {
//버전 선택시 셀렉트 박스 이니시에이터
$("#multiple-version").multipleSelect({
filter: true,
// selectBox 닫혔을 때
onClose: function () {
var versionTag = $("#multiple-version").val();
console.log("[ fullDataSheet :: makeVersionMultiSelectBox ] :: versionTag");
selectedPdServiceVersionIds = versionTag;
if (isEmpty(selectedPdServiceId)) {
jError("제품(서비스)가 선택되지 않았습니다.");
return;
}
if (isEmpty(versionTag)) {
jError("버전이 선택되지 않았습니다.");
// // assginee 선택을 초기화
$("#selected_assignee option").remove();
return;
}
// 실무자 선택
bind_Assignee_By_PdServiceVersions();
// 주차 선택 셀렉트 박스 갱신 (선택된 버전 기준)
let filteredVersionData = versionListData.filter((item) => versionTag.includes(item.c_id.toString()));
makeWeekSelectBox(filteredVersionData);
// 버전 변경 시 플레이어 초기화
resetAudioPlayer();
}
});
}
function bind_VersionData_By_PdService() {
$("#multiple-version option").remove();
$.ajax({
url: "/auth-user/api/arms/pdService/versions-with-date?c_id=" + $("#selected_pdService").val(),
type: "GET",
dataType: "json",
contentType: "application/json;charset=UTF-8",
progress: true,
statusCode: {
200: function (data) {
//////////////////////////////////////////////////////////
versionListData = [];
for (var k in data.response) {
var obj = data.response[k];
selectedPdServiceVersionIds.push(obj.c_id);
versionListData.push({
c_id: obj.c_id,
c_title: obj.c_title,
start_date: obj.c_pds_version_start_date,
end_date: obj.c_pds_version_end_date
});
var newOption = new Option(obj.c_title, obj.c_id, true, true);
$("#multiple-version").append(newOption);
}
//////////////////////////////////////////////////////////
$("#multiple-version").multipleSelect("refresh");
// 실무자 선택
bind_Assignee_By_PdServiceVersions();
// 주차 선택 셀렉트 박스 갱신
makeWeekSelectBox(versionListData);
}
}
});
}
////////////////////////////////////////////////////////////////////////////////////////
// 작업자 셀렉트 박스
////////////////////////////////////////////////////////////////////////////////////////
function makeAssigneeSelectBox() {
console.log("makeAssigneeSelectBox");
//버전 선택시 셀렉트 박스 이니시에이터
$("#selected_assignee").multipleSelect({
filter: true,
onClose: function () {
selectedAssigneeId = $("#selected_assignee option:selected").val();
selectedAssigneeName = $("#selected_assignee option:selected").text();
console.log("selectedAssigneeId: " + selectedAssigneeId);
console.log("selectedAssigneeName: " + selectedAssigneeName);
// 모든 선택이 완료되면 자동으로 AI 뉴스 오디오 생성
// fetchNewsAudio();
}
});
}
function bind_Assignee_By_PdServiceVersions() {
console.log("bind_Assignee_By_PdServiceVersions");
$("#selected_assignee option").remove();
$("#selected_assignee").append(new Option("Select Assignee", "", true, true));
$.ajax({
url: "/engine-search-api/assignees/rolling3m",
type: "POST",
dataType: "json",
data: JSON.stringify({
pdServiceId: selectedPdServiceId,
pdServiceVersions: selectedPdServiceVersionIds
}),
contentType: "application/json;charset=UTF-8",
statusCode: {
200: function (data) {
console.log(data);
//////////////////////////////////////////////////////////
for (let obj of data) {
let newOption = new Option(obj.displayName, obj.accountId, false, false);
$("#selected_assignee").append(newOption);
}
$("#selected_assignee").multipleSelect("refresh");
//////////////////////////////////////////////////////////
jSuccess("실무자 목록 조회가 완료되었습니다. (" + data.length + "명)");
},
error: function (e) {
jError("실무자 목록 조회 중 에러가 발생했습니다.");
}
}
});
}
////////////////////////////////////////
// 주차 선택 셀렉트 박스
////////////////////////////////////////
// 주차 선택 셀렉트 박스 초기화 (select2만 적용)
function initWeekSelectBox() {
$("#select-week").select2({
placeholder: "Select a Week",
templateResult: formatWeekOption
});
// 주차 선택 이벤트
$("#select-week").on("select2:select", function (e) {
var selectedOption = $(this).find("option:selected");
selectedValue = $(this).val(); // 예: "2026-W01"
startDate = selectedOption.data("startDate");
endDate = selectedOption.data("endDate");
// 미래 주차 선택 방지
var today = new Date();
today.setHours(0, 0, 0, 0);
if (startDate && new Date(startDate) > today) {
$("#select-week").val("").trigger("change");
jError("주간보고를 생성할 수 없는 주차입니다. 활성화된 주차를 선택해주세요.");
return;
}
console.log("[ reportPerformance :: initWeekSelectBox ] :: 선택된 주차 => ", selectedValue);
console.log("[ reportPerformance :: initWeekSelectBox ] :: 시작일 => ", startDate);
console.log("[ reportPerformance :: initWeekSelectBox ] :: 종료일 => ", endDate);
});
}
// 주차 옵션 포맷팅 함수 (현재/미래 주차 스타일 적용)
function formatWeekOption(option) {
if (!option.id) {
return option.text;
}
var $option = $(option.element);
var endDate = $option.data("endDate");
var $result = $("").text(option.text);
if (endDate) {
var today = new Date();
today.setHours(0, 0, 0, 0);
var weekEndDate = new Date(endDate);
if (weekEndDate >= today) {
$result.addClass("week-future");
} else {
$result.addClass("week-past");
}
}
return $result;
}
function makeWeekSelectBox(versionData) {
// 기존 옵션 제거
$("#select-week option").not(":first").remove();
$("#select-week").val("").trigger("change");
if (!versionData || versionData.length === 0) {
console.log("[ reportPerformance :: makeWeekSelectBox ] :: versionData가 없습니다.");
return;
}
// 가장 빠른 start_date와 가장 늦은 end_date 찾기
let minMaxDate = versionData.reduce(
(acc, curr) => {
const startDate = parseVersionDate(curr.start_date);
const endDate = parseVersionDate(curr.end_date);
if (!acc.min || startDate < acc.min) {
acc.min = startDate;
}
if (!acc.max || endDate > acc.max) {
acc.max = endDate;
}
return acc;
},
{ min: null, max: null }
);
console.log(
"[ reportPerformance :: makeWeekSelectBox ] :: minDate => " + minMaxDate.min + ", maxDate => " + minMaxDate.max
);
// 주차 목록 생성
let weeks = generateWeekRanges(minMaxDate.min, minMaxDate.max);
for (var i = 0; i < weeks.length; i++) {
var week = weeks[i];
var newOption = new Option(week.label, week.value, false, false);
$(newOption).data("startDate", week.startDate);
$(newOption).data("endDate", week.endDate);
$("#select-week").append(newOption);
}
$("#select-week").trigger("change");
console.log("[ reportPerformance :: makeWeekSelectBox ] :: 주차 목록 생성 완료, 총 " + weeks.length + "주");
}
// 버전 날짜 문자열 파싱 (yyyy/MM/dd HH:mm 형식)
function parseVersionDate(dateStr) {
if (!dateStr) return null;
// "2025/09/01 13:38" 형식 파싱
var parts = dateStr.split(" ")[0].split("/");
return new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]));
}
// ISO 주차 번호 계산 (월요일 시작 기준)
function getISOWeekNumber(date) {
var d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
var dayNum = d.getUTCDay() || 7; // 일요일을 7로 변환
d.setUTCDate(d.getUTCDate() + 4 - dayNum); // 목요일로 이동
var yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
var weekNo = Math.ceil(((d - yearStart) / 86400000 + 1) / 7);
return { year: d.getUTCFullYear(), week: weekNo };
}
// 해당 날짜가 속한 주의 월요일 구하기
function getMondayOfWeek(date) {
var d = new Date(date);
var day = d.getDay();
var diff = d.getDate() - day + (day === 0 ? -6 : 1); // 월요일로 조정
return new Date(d.setDate(diff));
}
// 해당 날짜가 속한 주의 일요일 구하기
function getSundayOfWeek(date) {
var monday = getMondayOfWeek(date);
var sunday = new Date(monday);
sunday.setDate(monday.getDate() + 6);
return sunday;
}
// 날짜 포맷팅 (YY.MM.DD)
function formatShortDate(date) {
var yy = String(date.getFullYear()).slice(-2);
var mm = String(date.getMonth() + 1).padStart(2, "0");
var dd = String(date.getDate()).padStart(2, "0");
return yy + "." + mm + "." + dd;
}
// 주차 범위 목록 생성
function generateWeekRanges(startDate, endDate) {
var weeks = [];
var currentMonday = getMondayOfWeek(startDate);
while (currentMonday <= endDate) {
var currentSunday = getSundayOfWeek(currentMonday);
var isoWeek = getISOWeekNumber(currentMonday);
// 라벨 생성: "26년 1주차(25.12.29~26.01.04)"
var label =
String(isoWeek.year).slice(-2) +
"년 " +
isoWeek.week +
"주차 (" +
formatShortDate(currentMonday) +
"~" +
formatShortDate(currentSunday) +
")";
weeks.push({
label: label,
value: isoWeek.year + "-W" + String(isoWeek.week).padStart(2, "0"),
startDate: new Date(currentMonday),
endDate: new Date(currentSunday),
year: isoWeek.year,
week: isoWeek.week
});
// 다음 주 월요일로 이동
currentMonday = new Date(currentMonday);
currentMonday.setDate(currentMonday.getDate() + 7);
}
return weeks;
}
////////////////////////////////////////
// PPT 섹션 반응형 스케일링
////////////////////////////////////////
function initPptScaling() {
updatePptScale();
$(window).on("resize", debounce(updatePptScale, 100));
}
function updatePptScale() {
var $wrapper = $("#ppt-scale-wrapper");
var $content = $("#ppt-content");
if ($wrapper.length === 0 || $content.length === 0) {
return;
}
var wrapperWidth = $wrapper.width();
var scale = wrapperWidth / PPT_ORIGINAL_WIDTH;
// 최대 스케일은 1 (원본 크기 이상으로 확대하지 않음)
scale = Math.min(scale, 1);
$content.css("transform", "scale(" + scale + ")");
// wrapper 높이 조정 (스케일된 높이에 맞춤)
var scaledHeight = PPT_ORIGINAL_HEIGHT * scale;
$wrapper.css("height", scaledHeight + "px");
}
// 디바운스 함수 (리사이즈 이벤트 최적화)
function debounce(func, wait) {
var timeout;
return function () {
var context = this,
args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function () {
func.apply(context, args);
}, wait);
};
}
////////////////////////////////////////
// PDF Viewer 제어
////////////////////////////////////////
// PDF 로드 함수
function loadPdfViewer(pdfUrl) {
var viewerUrl = PDF_VIEWER_BASE_URL + "?file=" + encodeURIComponent(pdfUrl) + "#zoom=page-width";
$("#pdf-viewer-iframe").attr("src", viewerUrl);
// 다운로드 버튼 업데이트
$("#btn-pdf-download").attr("href", pdfUrl).attr("download", pdfUrl.split("/").pop());
}
// PDF Viewer 초기화 (테스트용 PDF 로드)
function initPdfViewer() {
var testPdfUrl = "/arms/mock/default_template_ARMS_pdf.pdf";
loadPdfViewer(testPdfUrl);
}
// 보고서 표시 버튼 이벤트
function downloadPdfViewer() {
$("#btn_report_download").click(function () {
if (selectedReportType === "weekly") {
// 주간업무 보고
if (isEmpty(selectedPdServiceId)) {
jError("제품(서비스)가 선택되지 않았습니다.");
return;
}
if (isEmpty(selectedPdServiceVersionIds)) {
jError("버전이 선택되지 않았습니다.");
return;
}
if (isEmpty(selectedValue)) {
jError("주간보고를 위한 주(Week)를 선택해주세요.");
return;
}
ReportWeeklyApi.fetchPDF(
selectedPdServiceId,
selectedPdServiceTitle,
selectedPdServiceVersionIds.join(","),
selectedValue,
startDate,
endDate,
"inline"
);
} else {
// 개인업무 보고
fetchPerformanceWithAudio(selectedPdServiceId, selectedPdServiceTitle, selectedPdServiceVersionIds, selectedAssigneeId, selectedAssigneeName);
}
});
}
var fetchPDF = function (pdServiceId, pdServiceName, pdServiceVersionLinks, assigneeId, assigneeName) {
if (!isValidatedValues()) {
return;
}
function buildRequestDTOPDF(pdServiceId, pdServiceName,pdServiceVersionLinks, assigneeId, assigneeName) {
return {
"pdServiceId": Number(pdServiceId),
"pdServiceName": pdServiceName,
"pdServiceVersions": pdServiceVersionLinks,
"assignee": {
"accountId": assigneeId,
"name": assigneeName
},
"display": "attachment"
};
}
let dto = buildRequestDTOPDF(pdServiceId, pdServiceName, pdServiceVersionLinks, assigneeId, assigneeName);
// byte[] 다운로드를 위해 blob 타입으로 요청
return $.ajax({
url: `/auth-user/api/arms/report/personal/export/performance`,
type: "POST",
data: JSON.stringify(dto),
contentType: "application/json;charset=UTF-8",
xhrFields: {
responseType: "blob" // binary 데이터 수신
},
success: function (blob, status, xhr) {
// 이전 Blob URL 해제 (메모리 누수 방지)
if (previousBlobUrl) {
URL.revokeObjectURL(previousBlobUrl);
}
// Blob URL 생성
var blobUrl = URL.createObjectURL(blob);
previousBlobUrl = blobUrl;
// PDF.js viewer에 Blob URL 전달
var viewerBaseUrl = "../reference/jquery-plugins/pdfjs-3.11.174-dist/web/viewer.html";
var viewerUrl = viewerBaseUrl + "?file=" + encodeURIComponent(blobUrl) + "#zoom=page-width";
var contentDisposition = xhr.getResponseHeader("Content-Disposition");
var filename = "PersonalReport.pdf";
if (contentDisposition) {
var filenameMatch = contentDisposition.match(/filename\*?=(?:UTF-8'')?["']?([^"';\n]+)["']?/i);
if (filenameMatch && filenameMatch[1]) {
filename = decodeURIComponent(filenameMatch[1]);
}
}
// iframe src 업데이트
var $iframe = $("#pdf-viewer-iframe");
$iframe.attr("src", viewerUrl);
// iframe 로드 완료 후 커스텀 스타일 및 파일명 적용
$iframe.off("load.pdfStyle").on("load.pdfStyle", function () {
try {
var iframeDoc = this.contentDocument || this.contentWindow.document;
var customStyle = iframeDoc.createElement("style");
customStyle.textContent = ".dropdownToolbarButton > select, .toolbarField { font-size: 13px !important; }";
iframeDoc.head.appendChild(customStyle);
// pdfjs 내장 다운로드 버튼 파일명 설정
var pdfViewerApp = this.contentWindow.PDFViewerApplication;
if (pdfViewerApp) {
pdfViewerApp._contentDispositionFilename = filename;
}
} catch (e) {
console.warn("PDF viewer 스타일 적용 실패:", e);
}
});
// 다운로드 버튼 업데이트
$("#btn-pdf-download").attr("href", blobUrl).attr("download", filename);
console.log("[ ReportPerformanceApi :: fetchPDF ] :: PDF 로드 완료 => ", filename);
},
error: function (xhr, status, error) {
console.error("[ ReportPerformanceApi :: fetchPDF ] :: 가져오기 실패 => ", error);
jError("PDF 파일 가져오기에 실패했습니다.");
}
});
};
var fetchPerformanceWithAudio = function (pdServiceId, pdServiceName, pdServiceVersionLinks, assigneeId, assigneeName) {
if (!isValidatedValues()) {
return;
}
var dto = {
"pdServiceId": Number(pdServiceId),
"pdServiceName": pdServiceName,
"pdServiceVersions": pdServiceVersionLinks,
"assignee": {
"accountId": assigneeId,
"name": assigneeName
},
"display": "attachment"
};
// 오디오 로딩 표시
$("#audio-loading").show();
$("#audio-player-wrapper").hide();
$("#audio-status").hide();
$.ajax({
url: "/auth-user/api/arms/report/personal/export/performance-audio",
type: "POST",
data: JSON.stringify(dto),
contentType: "application/json;charset=UTF-8",
dataType: "json",
success: function (data) {
// --- PDF 처리 ---
var byteCharacters = atob(data.fileBase64);
var byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
var pdfBlob = new Blob([byteArray], { type: data.contentType || "application/pdf" });
if (previousBlobUrl) {
URL.revokeObjectURL(previousBlobUrl);
}
var blobUrl = URL.createObjectURL(pdfBlob);
previousBlobUrl = blobUrl;
var filename = data.fileName || "PersonalReport.pdf";
var viewerBaseUrl = "../reference/jquery-plugins/pdfjs-3.11.174-dist/web/viewer.html";
var viewerUrl = viewerBaseUrl + "?file=" + encodeURIComponent(blobUrl) + "#zoom=page-width";
var $iframe = $("#pdf-viewer-iframe");
$iframe.attr("src", viewerUrl);
$iframe.off("load.pdfStyle").on("load.pdfStyle", function () {
try {
var iframeDoc = this.contentDocument || this.contentWindow.document;
var customStyle = iframeDoc.createElement("style");
customStyle.textContent = ".dropdownToolbarButton > select, .toolbarField { font-size: 13px !important; }";
iframeDoc.head.appendChild(customStyle);
var pdfViewerApp = this.contentWindow.PDFViewerApplication;
if (pdfViewerApp) {
pdfViewerApp._contentDispositionFilename = filename;
}
} catch (e) {
console.warn("PDF viewer 스타일 적용 실패:", e);
}
});
$("#btn-pdf-download").attr("href", blobUrl).attr("download", filename);
console.log("[ ReportPerformanceApi :: fetchPerformanceWithAudio ] :: PDF 로드 완료 => ", filename);
// --- Audio 처리 ---
if (data.audioBase64) {
var audioBytes = atob(data.audioBase64);
var audioArray = new Uint8Array(audioBytes.length);
for (var j = 0; j < audioBytes.length; j++) {
audioArray[j] = audioBytes.charCodeAt(j);
}
var audioBlob = new Blob([audioArray], { type: "audio/wav" });
if (previousAudioBlobUrl) {
URL.revokeObjectURL(previousAudioBlobUrl);
}
previousAudioBlob = audioBlob;
previousAudioBlobUrl = URL.createObjectURL(audioBlob);
var audioPlayer = document.getElementById("news-audio-player");
audioPlayer.src = previousAudioBlobUrl;
$("#audio-loading").hide();
$("#audio-player-wrapper").show();
$("#btn-audio-download")
.attr("href", previousAudioBlobUrl)
.attr("download", "ai-news-audio.wav");
console.log("[ ReportPerformanceApi :: fetchPerformanceWithAudio ] :: 오디오 로드 완료");
} else {
$("#audio-loading").hide();
$("#audio-status").show().find("p").html(
' 오디오 데이터가 없습니다.'
);
}
},
error: function (xhr, status, error) {
console.error("[ ReportPerformanceApi :: fetchPerformanceWithAudio ] :: 가져오기 실패 => ", error);
jError("보고서 생성에 실패했습니다.");
$("#audio-loading").hide();
$("#audio-status").show().find("p").html(
' 오디오 생성에 실패했습니다.'
);
}
});
};
function isValidatedValues() {
if (isEmpty(selectedPdServiceId)) {
jError("제품(서비스)가 선택되지 않았습니다.");
return false;
}
if (isEmpty(selectedPdServiceVersionIds)) {
jError("버전이 선택되지 않았습니다.");
return false;
}
if (isEmpty(selectedAssigneeId)) {
jNotify("실무자를 선택하지 않았습니다.");
return false;
}
return true;
}
////////////////////////////////////////
// AI 서비스 상태 확인
////////////////////////////////////////
function checkAiServiceStatus() {
$.ajax({
global: false,
url: "/ai-api/status",
type: "GET",
dataType: "json",
contentType: "application/json;charset=UTF-8",
success: function (data) {
aiServiceStatus = data;
applyAiServiceStatus();
},
error: function (xhr, status, error) {
aiServiceStatus = { ttsAvailable: false };
applyAiServiceStatus();
}
});
}
function applyAiServiceStatus() {
if (!aiServiceStatus.ttsAvailable) {
$("#audio-status").show().find("p").html(
' 현재 AI 오디오 서비스를 이용할 수 없습니다.'
);
}
}
////////////////////////////////////////
// AI News Audio Player
////////////////////////////////////////
function fetchNewsAudio() {
if (aiServiceStatus && !aiServiceStatus.ttsAvailable) {
return;
}
if (isEmpty(selectedPdServiceId) || isEmpty(selectedPdServiceVersionIds) || isEmpty(selectedAssigneeId)) {
return;
}
$("#audio-loading").show();
$("#audio-player-wrapper").hide();
$("#audio-status").hide();
var dto = {
"pdServiceId": Number(selectedPdServiceId),
"pdServiceVersions": selectedPdServiceVersionIds,
"assigneeId": selectedAssigneeId,
"message": "KPI"
};
$.ajax({
global: false,
url: "/ai-api/performance/news-audio",
type: "POST",
data: JSON.stringify(dto),
contentType: "application/json;charset=UTF-8",
xhrFields: {
responseType: "blob"
},
success: function (blob) {
if (previousAudioBlobUrl) {
URL.revokeObjectURL(previousAudioBlobUrl);
}
previousAudioBlob = blob;
previousAudioBlobUrl = URL.createObjectURL(blob);
var audioPlayer = document.getElementById("news-audio-player");
audioPlayer.src = previousAudioBlobUrl;
$("#audio-loading").hide();
$("#audio-player-wrapper").show();
$("#btn-audio-download")
.attr("href", previousAudioBlobUrl)
.attr("download", "ai-news-audio.wav");
console.log("[ ReportPerformance :: fetchNewsAudio ] :: 오디오 생성 완료");
},
error: function () {
$("#audio-loading").hide();
$("#audio-player-wrapper").hide();
$("#audio-status").show().find("p").html(
' 오디오 생성에 실패했습니다.'
);
}
});
}
function resetAudioPlayer() {
var audioPlayer = document.getElementById("news-audio-player");
if (audioPlayer) {
audioPlayer.pause();
audioPlayer.src = "";
}
if (previousAudioBlobUrl) {
URL.revokeObjectURL(previousAudioBlobUrl);
previousAudioBlobUrl = null;
}
previousAudioBlob = null;
$("#audio-loading").hide();
$("#audio-player-wrapper").hide();
$("#btn-audio-download").attr("href", "#").removeAttr("download");
if (aiServiceStatus && !aiServiceStatus.ttsAvailable) {
$("#audio-status").show().find("p").html(
' AI 음성 서비스가 일시적으로 중단되어 텍스트 보고서만 제공됩니다'
);
} else {
$("#audio-status").show().find("p").html(
' 보고서 생성 시 AI 음성 요약이 함께 제공됩니다'
);
}
}
////////////////////////////////////////////////////////////////////////////////////////
// 보고서 유형 선택 토글
////////////////////////////////////////////////////////////////////////////////////////
function initReportTypeToggle() {
$("#report-btn-group label").on("click", function () {
var $clicked = $(this);
var isPersonal = $clicked.find("#personal-option").length > 0;
var isWeekly = $clicked.find("#weekly-option").length > 0;
if (isPersonal) {
console.log("[ reportPerformance :: initReportTypeToggle ] :: 개인업무 보고 선택");
$("#assignee-section").removeClass("hidden");
$("#weekly-section").addClass("hidden");
selectedReportType = "personal";
} else if (isWeekly) {
console.log("[ reportPerformance :: initReportTypeToggle ] :: 주간업무 보고 선택");
$("#weekly-section").removeClass("hidden");
$("#assignee-section").addClass("hidden");
selectedReportType = "weekly";
}
// 유형 변경 시 플레이어 초기화
resetAudioPlayer();
});
}