[정보] IT정보&활용 2024. 11. 29. 18:21

엑셀로 IPA 분석하기, VBA 활용

반응형

IPA(Importance Performance Analysis) 분석이란 응답자가 지각하는 특정 요소에 대한 성과(만족도)와 중요도를 각각 X와 Y축으로 하는 2차원상의 평면위에 좌표로 각 요소를 표현하는 분석방법을 말합니다.

응답자가 인식하는 중요도와 다른 하나의 변수(만족도, 성과, 필요도 등)와 비교하는 것인데, 같은 질문에 대해 두가지 속성으로 물어보는 경우 사분면으로 그려서 시각적으로 보여줄 수 있어 유용합니다.

 

방법은 항목별 평균을 전체평균을 기준으로 그보다 높은가 낮은가를 비교하는 방식이라 구현이 그렇게 어려운 것은 아닙니다.

다만 그래프로 시각화하는 과정이 손이 많이 가는 바, VBA로 그래프를 만드는 과정을 자동화해 보았습니다.

IPA 분석 결과
IPA 분석_vba v2024-1130.xlsm
0.06MB

반응형
[정보] IT정보&활용 2024. 11. 6. 12:10

PDF 나누기(split)

반응형

PDF 나누기.html
0.01MB

 

PDF를 두개로 나누는 html입니다.

 

pdf-lib (MIT 라이선스): https://github.com/Hopding/pdf-lib
pdf.js (Apache 2.0 라이선스): https://github.com/mozilla/pdf.js
위 라이브러리를 사용하였으며, chatGPT 등의 도움을 받아 실습 삼아 만들어 보았습니다.

 

아래는 html 스크립트 입니다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>PDF 나누기 (Split)</title>

    <!-- pdf-lib 및 pdf.js 라이브러리 로드 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf-lib/1.17.1/pdf-lib.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.10.377/pdf.min.js"></script>

    <style>
        body {
            font-family: Arial, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background-color: #f3f4f6;
            margin: 0;
        }
        .container {
            width: 100%;
            max-width: 500px;
            padding: 20px;
            background: white;
            border-radius: 8px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
        }
        h1 {
            font-size: 24px;
            color: #333;
            text-align: center;
        }
        p {
            font-size: 14px;
            color: #555;
            text-align: left;
        }
        input[type="file"] {
            width: 100%;
            padding: 10px;
            margin: 10px 0;
            font-size: 14px;
        }
        input[type="number"] {
            width: 50%;
            padding: 10px;
            margin: 10px 0;
            font-size: 14px;
        }
        #split-button {
            width: 100%;
            padding: 12px;
            background-color: #28a745;
            color: white;
            border: none;
            border-radius: 5px;
            font-size: 16px;
            cursor: pointer;
            margin-top: 10px;
        }
        #split-button:hover {
            background-color: #218838;
        }
        footer {
            text-align: left;
            font-size: 12px;
            color: #777;
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>PDF 나누기</h1>
        <p>PDF를 두 개의 파일로 나눌 수 있습니다.</p>
        <p>1. "파일 선택" 버튼을 눌러 나눌 PDF 파일을 선택하세요.</p>
        <input type="file" id="file-input" accept="application/pdf" />

        <p>2. 나눌 페이지 번호를 입력하세요. <br>
             &nbsp;&nbsp;&nbsp;&nbsp;(예: 3을 입력하면 1-3페이지와 4-마지막 페이지로 나뉩니다.)</p>
        <input type="number" id="split-page" min="1" />

        <p>3. "PDF 나누기" 버튼을 누르세요.</p>
        <button id="split-button">PDF 나누기</button>

        <footer>
            <p><strong>사용된 라이브러리</strong></p>
            <ul>
                <li>pdf-lib (MIT 라이선스): <a href="https://github.com/Hopding/pdf-lib">https://github.com/Hopding/pdf-lib</a></li>
                <li>pdf.js (Apache 2.0 라이선스): <a href="https://github.com/mozilla/pdf.js">https://github.com/mozilla/pdf.js</a></li>
            </ul>
        </footer>
    </div>

<script>
    const fileInput = document.getElementById('file-input');
    const splitPage = document.getElementById('split-page');
    const splitButton = document.getElementById('split-button');

    splitButton.addEventListener('click', async () => {
        if (!fileInput.files[0]) {
            alert('PDF 파일을 선택해주세요.');
            return;
        }

        const pageNumber = parseInt(splitPage.value);
        if (isNaN(pageNumber) || pageNumber < 1) {
            alert('유효한 페이지 번호를 입력해주세요.');
            return;
        }

        const file = fileInput.files[0];
        const arrayBuffer = await file.arrayBuffer();
        const pdfDoc = await PDFLib.PDFDocument.load(arrayBuffer);

        if (pageNumber >= pdfDoc.getPageCount()) {
            alert('입력한 페이지 번호가 PDF의 총 페이지 수보다 크거나 같습니다.');
            return;
        }

        // 첫 번째 PDF 생성 (1부터 pageNumber까지)
        const firstPdf = await PDFLib.PDFDocument.create();
        const firstPages = await firstPdf.copyPages(pdfDoc, Array.from({length: pageNumber}, (_, i) => i));
        firstPages.forEach(page => firstPdf.addPage(page));

        // 두 번째 PDF 생성 (pageNumber+1부터 끝까지)
        const secondPdf = await PDFLib.PDFDocument.create();
        const secondPages = await secondPdf.copyPages(pdfDoc, Array.from({length: pdfDoc.getPageCount() - pageNumber}, (_, i) => i + pageNumber));
        secondPages.forEach(page => secondPdf.addPage(page));

        // PDF 저장 및 다운로드
        const firstPdfBytes = await firstPdf.save();
        const secondPdfBytes = await secondPdf.save();

        downloadPdf(firstPdfBytes, 'split_first.pdf');
        downloadPdf(secondPdfBytes, 'split_second.pdf');
    });

    function downloadPdf(pdfBytes, fileName) {
        const blob = new Blob([pdfBytes], { type: 'application/pdf' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = fileName;
        a.click();
        URL.revokeObjectURL(url);
    }
</script>

</body>
</html>

반응형
[정보] IT정보&활용 2024. 11. 1. 13:10

PDF 파일 합치기(수정)

반응형
더보기

디자인을 가미하기 전 순수 스크립트만 담겨있는 초기 버전입니다.

소스 검토용으로 남겨놓습니다.

PDF 합치기.html
0.00MB

PDF 합치기 v2.html
0.01MB

 

위 파일을 자신의 컴퓨터로 다운 받아서 그냥 실행하시면 됩니다.

 

 

 

pdf-lib (MIT 라이선스): https://github.com/Hopding/pdf-lib
pdf.js (Apache 2.0 라이선스): https://github.com/mozilla/pdf.js

 

위 두 개의 라이브러리를 사용하였으며, chatGPT 등의 도움을 받아 실습 삼아 만들어 보았습니다.

 

아래는 html 스크립트입니다.

---------------------------------------------

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>PDF 합치기 (Merge)</title>

    <!-- pdf-lib 및 pdf.js 라이브러리 로드 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf-lib/1.17.1/pdf-lib.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.10.377/pdf.min.js"></script>

    <!-- 스타일링 -->
    <style>
        body {
            font-family: Arial, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background-color: #f3f4f6;
            margin: 0;
        }
        .container {
            width: 100%;
            max-width: 500px;
            padding: 20px;
            background: white;
            border-radius: 8px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
        }
        h1 {
            font-size: 24px;
            color: #333;
            text-align: center;
        }
        p {
            font-size: 14px;
            color: #555;
            text-align: left;
        }
        input[type="file"] {
            width: 100%;
            padding: 10px;
            margin: 10px 0;
            font-size: 14px;
        }
        #file-list {
            list-style: none;
            padding: 0;
            margin: 10px 0;
        }
        #file-list li {
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 5px;
            margin: 5px 0;
            display: flex;
            justify-content: space-between;
            align-items: center;
            background-color: #f9f9f9;
        }
        .move-buttons button {
            padding: 5px;
            margin: 0 2px;
            background: #4a90e2;
            color: white;
            border: none;
            border-radius: 3px;
            cursor: pointer;
            font-size: 12px;
        }
        .move-buttons button:hover {
            background: #357ABD;
        }
        #merge-button {
            width: 100%;
            padding: 12px;
            background-color: #28a745;
            color: white;
            border: none;
            border-radius: 5px;
            font-size: 16px;
            cursor: pointer;
            margin-top: 10px;
        }
        #merge-button:hover {
            background-color: #218838;
        }
        footer {
            text-align: left;
            font-size: 12px;
            color: #777;
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>PDF 합치기</h1>
        <p>여러 개의 PDF를 하나로 합칠 수 있습니다.</p>
        <p>1. "파일 선택" 버튼을 눌러 합칠 파일을 선택하세요.</p>
        <input type="file" id="file-input" multiple accept="application/pdf" />

        <p>2. 아래 목록에서 ↑↓를 눌러 순서를 조정하세요.</p>
        <ul id="file-list"></ul>

        <p>3. 순서를 확인한 후 "PDF 합치기" 버튼을 누르세요.</p>
        <button id="merge-button">PDF 합치기</button>

        <footer>
            <p><strong>사용된 라이브러리</strong></p>
            <ul>
                <li>pdf-lib (MIT 라이선스): <a href="https://github.com/Hopding/pdf-lib">https://github.com/Hopding/pdf-lib</a></li>
                <li>pdf.js (Apache 2.0 라이선스): <a href="https://github.com/mozilla/pdf.js">https://github.com/mozilla/pdf.js</a></li>
            </ul>
        </footer>
    </div>

<script>
    const fileInput = document.getElementById('file-input');
    const fileList = document.getElementById('file-list');

    fileInput.addEventListener('change', () => {
        fileList.innerHTML = '';
        Array.from(fileInput.files).forEach((file, index) => {
            const li = document.createElement('li');
            li.textContent = file.name;
            li.dataset.index = index;

            // 이동 버튼 추가
            const moveButtons = document.createElement('div');
            moveButtons.classList.add('move-buttons');

            const upButton = document.createElement('button');
            upButton.textContent = '↑';
            upButton.addEventListener('click', () => moveItem(li, -1));

            const downButton = document.createElement('button');
            downButton.textContent = '↓';
            downButton.addEventListener('click', () => moveItem(li, 1));

            // 삭제 버튼 추가
            const deleteButton = document.createElement('button');
            deleteButton.textContent = '삭제';
            deleteButton.style.background = '#dc3545';
            deleteButton.style.marginLeft = '5px';
            deleteButton.addEventListener('click', () => li.remove());

            moveButtons.appendChild(upButton);
            moveButtons.appendChild(downButton);
            moveButtons.appendChild(deleteButton);
            li.appendChild(moveButtons);

            fileList.appendChild(li);
        });
    });

    function moveItem(item, direction) {
        const sibling = (direction === -1) ? item.previousElementSibling : item.nextElementSibling;
        if (sibling) {
            fileList.insertBefore(item, direction === -1 ? sibling : sibling.nextElementSibling);
        }
    }

    document.getElementById('merge-button').addEventListener('click', async () => {
        const files = Array.from(fileList.children).map(li => fileInput.files[li.dataset.index]);
        const mergedPdf = await PDFLib.PDFDocument.create();

        for (let file of files) {
            const arrayBuffer = await file.arrayBuffer();
            const pdfDoc = await PDFLib.PDFDocument.load(arrayBuffer);
            const pages = await mergedPdf.copyPages(pdfDoc, pdfDoc.getPageIndices());
            pages.forEach(page => mergedPdf.addPage(page));
        }

        const pdfBytes = await mergedPdf.save();
        const blob = new Blob([pdfBytes], { type: 'application/pdf' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = 'merged.pdf';
        a.click();
        URL.revokeObjectURL(url);
    });
</script>

</body>
</html>

반응형
[정보] IT정보&활용 2024. 10. 23. 18:56

[구글 Apps Script] PDF로 출력하기

반응형

[구글 스프레드 시트]로 일지를 만든 경우, 이 일지의 날짜를 하나하나 변경하면서 PDF로 출력해 저장해주는 스크립트 입니다.

이 모두를 묶어서 하나의 PDF로 구현하는 것을 고민하였으나, 아직까지 구글에서는 PDF 합치기 기능은 제공하고 있지 않는거 같습니다.

 

메뉴바에 출력 버튼이 만들어지고, 적용 월을 입력하면, 그 달에 해당하는 일지를 모두 PDF로 만들어줍니다.

단, 한꺼번에 많은 양을 PDF로 변환하면 구글에서 오류 메시지를 나타내었습니다.

해서 10개씩 출력한 후 30초 기다렸다가 다시 출력하도록 했습니다.

그래서 모두 PDF로 변환되는데 3분 안쪽의 시간이 필요합니다.

// 이후의 각주를 참고하셔서 자신의 시트에 맞게 수정해서 사용하시면 됩니다.

또한 PDF가 저장될 폴더에 접근 권한을 열어 두는거 잊지마세요~

 

----------------------------------

Code.gs

----------------------------------

function onOpen() {

  var ui = SpreadsheetApp.getUi();

  ui.createMenu('출력')

    .addItem('월 입력', 'generatePDFsByMonth')

    .addToUi();

}

 

function generatePDFsByMonth() {

  var ss = SpreadsheetApp.getActiveSpreadsheet();

  var sheet1Name = 'DATA';

  var sheet2Name = '일지'; // 출력서식이 있는 시트명을 정확히 지정해주세요.

  var dateCell = 'A3'; // '일지' 시트에서 날짜가 입력되는 셀입니다.

  var outputRange = 'A1:G25'; // '일지' 시트의 출력범위를 조정합니다.

  var dataRange = 'A2:A'; // 'DATA'시트에서의 날짜 데이터 범위

  var folderId = '1OlXGiKJyeixZ0j4PLIkWl1xxffBZVQZ0'; // PDF가 저장될 폴더의 ID입니다.

 

  var sheet1 = ss.getSheetByName(sheet1Name);

  var sheet2 = ss.getSheetByName(sheet2Name);

  var folder = DriveApp.getFolderById(folderId);

  var ui = SpreadsheetApp.getUi();

  var response = ui.prompt('월을 입력하세요 (YYYY-MM)', ui.ButtonSet.OK_CANCEL);

 

  if (response.getSelectedButton() == ui.Button.OK) {

    var month = response.getResponseText().trim();

    var data = sheet1.getRange(dataRange + sheet1.getLastRow()).getValues();

    var filteredData = data.filter(function(row) {

      return row[0] && formatDate(row[0]).includes(month);

    });

 

    for (var i = 0; i < filteredData.length; i += 10) {

      var chunk = filteredData.slice(i, i + 10);

      chunk.forEach(function(row) {

        sheet2.getRange(dateCell).setValue(formatDate(row[0]));

        SpreadsheetApp.flush();

        Utilities.sleep(1000); // 데이터를 로드하는 데 충분한 시간을 두기 위해 1초 대기

        var pdfBlob = createPDF(sheet2, formatDate(row[0]) + '_' + sheet2Name + '.pdf', outputRange, folder);

        folder.createFile(pdfBlob);

      });

      Utilities.sleep(30000); // 10개의 파일 생성 후 30초 대기

    }

 

    if (filteredData.length > 0) {

      ui.alert('PDF 저장이 완료되었습니다: ' + month);

    } else {

      ui.alert('입력된 월에 해당하는 데이터가 없습니다: ' + month);

    }

  }

}

 

function formatDate(date) {

  var d = new Date(date);

  var year = d.getFullYear();

  var month = ('0' + (d.getMonth() + 1)).slice(-2);

  var day = ('0' + d.getDate()).slice(-2);

  return year + '-' + month + '-' + day;

}

 

function createPDF(sheet, fileName, range, folder) {

  var sheetName = sheet.getName();

  var url = 'https://docs.google.com/spreadsheets/d/' + SpreadsheetApp.getActiveSpreadsheet().getId() +

            '/export?exportFormat=pdf&format=pdf' +

            '&size=A4' +

            '&portrait=true' +

            '&fitw=true' +

            '&sheetnames=false&printtitle=false' +

            '&pagenumbers=false' +

            '&gridlines=false' +

            '&fzr=false' +

            '&gid=' + sheet.getSheetId() +

            '&range=' + range;

 

  var token = ScriptApp.getOAuthToken();

  var response = UrlFetchApp.fetch(url, {

    headers: {

      'Authorization': 'Bearer ' + token

    }

  });

 

  var pdfBlob = response.getBlob().setName(fileName);

  return pdfBlob;

}



반응형

구글 Apps Script로 후원신청서 만들기

반응형

구글 Apps Script로 입력폼을 만들고, 입력된 데이터들이 구글 스프레드 시트에 저장되도록 하는 스크립트를 구현해 보았습니다.

 

Apps Script는 두 개의 파일로 작동합니다.

Code.gs라는 이름의 스크립트 파일과 웹화면을 통해 보여질 index.html이 그것입니다.

 

※ 이 스크립트는 기본적으로 구글 스프레드 시트의 Apps Script를 다룰 줄 아는 사람을 대상으로 하기 때문에 스크립트 적용 이전단계에 대해서는 설명하지 않습니다.

 

Code.gs

 
// 스프레드시트 ID와 시트 이름, 서명이미지 저장 폴더 ID를 변수로 선언
var SPREADSHEET_ID = ' YOUR_SPREADSHEET_ID';
var SHEET_NAME = '시트1';
var FOLDER_ID = ' YOUR_FOLDER_ID';

function doGet() {
  return HtmlService.createHtmlOutputFromFile('index');
}

function sendUserData(name, gender, ssn_a, ssn_b, cphone_n, phone_n, address, e_mail, s_term, s_type, s_money, s_how, s_item, s_item_q, s_unit, s_sector, s_why, s_path, receipt1, receipt2, a_date, signature) {
  var sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(SHEET_NAME);
  var imageUrl = saveSignatureToDrive(signature, name);
  sheet.appendRow([
    name, gender, ssn_a, ssn_b,
    "'" + cphone_n, // 앞에 작은 따옴표 추가
    "'" + phone_n,  // 앞에 작은 따옴표 추가
    address, e_mail, s_term, s_type, s_money, s_how,
    s_item, s_item_q, s_unit, s_sector, s_why, s_path,
    receipt1, receipt2, a_date, '=IMAGE("' + imageUrl + '")'
  ]);
}

function saveSignatureToDrive(signature, name) {
  var folder = DriveApp.getFolderById(FOLDER_ID);
  var blob = Utilities.newBlob(Utilities.base64Decode(signature.split(',')[1]), 'image/png', name + '_signature.png');
  var file = folder.createFile(blob);
  var fileId = file.getId();
}

 

index.html

<!DOCTYPE html>
<html>
<head>
  <base target="_top">
  <title>부민노인복지관 후원신청서</title>
  <style>
    #sig {
      background-color: #e0e0e0;
      border: 1px solid black;
      width: 500px;
      height: 300px;
    }

    #s_item_q, #ssn_a {
      width: 6em;
    }

    #ssn_b {
      width: 7em;
    }

    #savingMessage {
      display: none;
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background-color: white;
      border: 1px solid black;
      padding: 20px;
      z-index: 1000;
    }

  </style>
</head>
<body>
  <p>부민노인복지관 후원신청서</p>
  <form id="donationForm">
    <label>이름</label>
    <input type="text" id="name" name="name" placeholder="이름" required><br>

    <label>성별</label>
    <input type="radio" name="gender" value="남">남자
    <input type="radio" name="gender" value="여">여자<br>

    <label>주민등록번호</label>
    <input type="text" id="ssn_a" name="ssn_a" size="6" maxlength="6" required>-
    <input type="password" id="ssn_b" name="ssn_b" size="7" maxlength="7" required><br>

    <label>휴대전화</label>
    <input type="text" id="cphone_n" name="cphone_n" size="11" maxlength="11"> ※ 숫자만 입력<br>

    <label>일반전화</label>
    <input type="text" id="phone_n" name="phone_n" size="11" maxlength="11"> ※ 숫자만 입력<br>

    <label>주소</label>
    <input type="text" id="address" name="address" size="30"><br>

    <label>이메일</label>
    <input type="text" id="e_mail" name="e_mail" size="30"><br>

    <label>후원방식</label>
    <input type="radio" name="s_term" value="정기">정기
    <input type="radio" name="s_term" value="비정기">비정기<br>

    <label>후원구분</label>
    <input type="radio" name="s_type" value="지정">지정
    <input type="radio" name="s_type" value="비지정">비지정<br>

    <label>후원금</label>
    <input type="text" id="s_money" name="s_money"><br>

    <label>후원방법</label>
    <input type="radio" name="s_how" value="자동이체">자동이체
    <input type="radio" name="s_how" value="현금납부">현금납부
    <input type="radio" name="s_how" value="CMS">CMS 이체
    <input type="radio" name="s_how" value="기타">기타<br>

    <label>후원품</label>
    (품명) <input type="text" id="s_item" name="s_item"> /
    (수량) <input type="number" id="s_item_q" name="s_item_q" size="4"> /
    (단위) <select id="s_unit" name="s_unit">
            <option value="">---</option>
            <option value="개"></option>
            <option value="세트">세트</option>
            <option value="Box">Box</option>  
            <option value="건"></option>  
            <option value="Kg">Kg</option>  
            <option value="명"></option>  
            <option value="포"></option>  
            <option value="g">g(그램)</option>  
            <option value="리터">리터</option>  
            <option value="대"></option>  
            <option value="장"></option>  
            <option value="통"></option>  
            <option value="판"></option>  
            <option value="병"></option>  
            <option value="마리">마리</option>  
            <option value="봉지">봉지</option>  
            <option value="팩"></option>  
            <option value="묶음">묶음</option>  
            <option value="그루">그루</option>  
            <option value="송이">송이</option>  
            <option value="포기">포기</option>  
            <option value="자루">자루</option>  
            <option value="인분">인분</option>  
            <option value="짝"></option>  
            <option value="포대">포대</option>  
            <option value="기타">기타</option>  
    </select><br>

    <label>후원분야</label>
    <select id="s_sector" name="s_sector">
      <option value="">---------</option>
      <option value="밑반찬지원">밑반찬지원</option>
      <option value="저소득노인 무료급식사업">저소득노인 무료급식사업</option>
      <option value="위기 및 취약노인 사업">위기 및 취약노인 사업</option>
      <option value="지역복지 연계사업">지역복지 연계사업</option>
      <option value="명절 정나눔 행사">명절 정나눔 행사</option>
      <option value="기타">기타</option>
    </select><br>

    <label>참여동기</label>
    <select id="s_why" name="s_why">
      <option value="">---------</option>
      <option value="경제적 여유가 있어서">경제적 여유가 있어서</option>
      <option value="불우한 이웃을 돕기위해">불우한 이웃을 돕기위해</option>
      <option value="지역사회복지를 위해">지역사회복지를 위해</option>
      <option value="종교적 신념으로">종교적 신념으로</option>
      <option value="기타">기타</option>
    </select><br>

    <label>참여경로</label>
    <select id="s_path" name="s_path">
      <option value="">---------</option>
      <option value="기존 후원자/자원봉사자 소개를 통해">기존 후원자/자원봉사자 소개를 통해</option>
      <option value="복지관 소식지나 안내물을 통해">복지관 소식지나 안내물을 통해</option>
      <option value="언론매체를 통해">언론매체를 통해(TV, 라디오, 신문 등)</option>
      <option value="특정 행사 및 기념일 축하">특정 행사 및 기념일 축하</option>
      <option value="기타">기타</option>
    </select><br>

    <label>후원금영수증 발급</label><br>
      <input type="checkbox" name="receipt1" value="후원금영수증 필요">후원금 영수증 필요<br>
      <input type="checkbox" name="receipt2" value="국세청 연말정산 간소화 후원내역 제공 동의">국세청 연말정산 간소화 후원내역 제공 동의 <br>

    <label>신청일자</label>
    <input type="date" id="a_date" name="a_date"><br>

    <label>서명</label><br>
    <canvas id="sig"></canvas><br>
    <button type="button" id="clearSig">서명 지우기</button><br><br>

    <button type="button" onclick="submitData()">제출</button>
  </form>

  <div id="savingMessage">저장 중입니다...</div>

  <script>
    var signaturePad;

    function setupSignatureBox() {
      var canvas = document.getElementById("sig");
      canvas.width = 500;  // 명시적으로 크기 설정
      canvas.height = 300; // 명시적으로 크기 설정
      signaturePad = new SignaturePad(canvas);
    }

    function clearSignature() {
      signaturePad.clear();
    }

    function submitData() {
      const form = document.getElementById('donationForm');
      const formData = new FormData(form);

      // 필수 입력 필드 확인
      if (!formData.get('name') || !formData.get('ssn_a') || !formData.get('ssn_b') || !formData.get('cphone_n') || signaturePad.isEmpty()) {
        alert('성명, 주민등록번호, 서명은 필수 입력 항목입니다.');
        return;
      }

      const data = {
        name: formData.get('name'),
        gender: formData.get('gender'),
        ssn_a: formData.get('ssn_a'),
        ssn_b: formData.get('ssn_b'),
        cphone_n: formData.get('cphone_n'),
        phone_n: formData.get('phone_n'),
        address: formData.get('address'),
        e_mail: formData.get('e_mail'),
        s_term: formData.get('s_term'),
        s_type: formData.get('s_type'),
        s_money: formData.get('s_money'),
        s_how: formData.get('s_how'),
        s_item: formData.get('s_item'),
        s_item_q: formData.get('s_item_q'),
        s_unit: formData.get('s_unit'),
        s_sector: formData.get('s_sector'),
        s_why: formData.get('s_why'),
        s_path: formData.get('s_path'),
        receipt1: formData.get('receipt1') ? '필요' : '불필요',
        receipt2: formData.get('receipt2') ? '동의' : '미동의',
        a_date: formData.get('a_date'),
        signature: signaturePad.toDataURL()
      };

      document.getElementById('savingMessage').style.display = 'block'; // 저장 중 메시지 표시

      google.script.run.withSuccessHandler(() => {
        document.getElementById('savingMessage').style.display = 'none'; // 저장 중 메시지 숨기기
        alert('데이터가 성공적으로 저장되었습니다.');
      }).withFailureHandler(error => {
        document.getElementById('savingMessage').style.display = 'none'; // 저장 중 메시지 숨기기
        console.error('Error:', error);
        alert('데이터 저장 중 오류가 발생했습니다. 다시 입력해주세요.');
      }).sendUserData(
        data.name, data.gender, data.ssn_a, data.ssn_b, data.cphone_n, data.phone_n,
        data.address, data.e_mail, data.s_term, data.s_type, data.s_money, data.s_how,
        data.s_item, data.s_item_q, data.s_unit, data.s_sector, data.s_why, data.s_path,
        data.receipt1, data.receipt2, data.a_date, data.signature
      );
    }

    document.getElementById("clearSig").addEventListener("click", clearSignature);
    document.addEventListener("DOMContentLoaded", setupSignatureBox);
  </script>
</body>
</html>

 

입력 폼은 아래 링크와 같이 보여지게 됩니다.

https://script.google.com/a/macros/bmsenior.org/s/AKfycbxOwy1AJqVZypRUhlJtNl3xzWDSm4mUWhKxtBwI0_FrT66QLX_6ruxFn0h0WR4JUYrA3w/exec

 

https://script.google.com/a/macros/bmsenior.org/s/AKfycbxOwy1AJqVZypRUhlJtNl3xzWDSm4mUWhKxtBwI0_FrT66QLX_6ruxFn0h0WR4JUYrA3w/exec

 

script.google.com

 

반응형