/**
* 웹 앱 요청이 들어왔을 때 실행되는 함수입니다.
* index.html 파일을 렌더링하여 반환합니다.
*/
function doGet() {
return HtmlService.createTemplateFromFile('index')
.evaluate()
.setTitle('김해제일고 3학년 10반 학부모 상담 신청');
}
/**
* HTML 템플릿의 스크립트릿에서 포함된 파일을 로드할 수 있도록 합니다.
* 예를 들어, include('file')과 같이 사용할 수 있습니다.
*/
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
/**
* 클라이언트 측에서 호출될 수 있는 함수입니다.
* 간단한 환영 메시지를 반환합니다. (현재는 사용되지 않음)
*/
function getHelloMessage() {
return "안녕하세요, Google Apps Script 웹 앱입니다!";
}
/**
* 클라이언트에서 상담 신청 데이터를 받아 Google Sheet에 기록하는 함수입니다.
* @param {Object} formData - 클라이언트에서 전송된 폼 데이터 객체
* - studentName: 학생 이름
* - consultDate: 상담 희망 날짜 (예: "9.8.")
* - consultTime: 상담 희망 시간 (예: "19:00-20:00")
* - consultContent: 상담 내용
*/
function processConsultationRequest(formData) {
// 스크립트가 연결된 활성 스프레드시트를 가져옵니다.
// 특정 스프레드시트 ID로 열려면:
// const spreadsheet = SpreadsheetApp.openById('YOUR_SPREADSHEET_ID_HERE');
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
// 데이터를 기록할 시트 이름을 지정합니다. (여기서는 '시트1'로 가정)
const sheetName = '시트1'; // <-- 이 부분을 실제 시트 이름으로 변경하세요!
const sheet = spreadsheet.getSheetByName(sheetName);
if (!sheet) {
throw new Error('"' + sheetName + '" 시트를 찾을 수 없습니다. 시트 이름을 확인해주세요.');
}
// 1. 날짜에 따라 열 인덱스 결정
let columnIndex; // Google Sheet의 B열은 인덱스 2, C열은 3 등
switch (formData.consultDate) {
case '9.8.':
columnIndex = 2; // B열
break;
case '9.9.':
columnIndex = 3; // C열
break;
case '9.10.':
columnIndex = 4; // D열
break;
case '9.11.':
columnIndex = 5; // E열
break;
case '9.12.':
columnIndex = 6; // F열
break;
default:
// 유효하지 않은 날짜가 넘어온 경우 에러 처리
Logger.log('유효하지 않은 상담 날짜: ' + formData.consultDate);
throw new Error('유효하지 않은 상담 날짜입니다.');
}
// 2. 시간에 따라 행 인덱스 결정
let rowIndex; // Google Sheet의 3행은 인덱스 3, 4행은 4
switch (formData.consultTime) {
case '19:00-20:00':
rowIndex = 3;
break;
case '20:00-21:00':
rowIndex = 4;
break;
default:
// 유효하지 않은 시간이 넘어온 경우 에러 처리
Logger.log('유효하지 않은 상담 시간: ' + formData.consultTime);
throw new Error('유효하지 않은 상담 시간입니다.');
}
// 3. 해당 셀에 학생 이름 입력
const targetCell = sheet.getRange(rowIndex, columnIndex);
// 셀이 비어있는지 확인하여 중복 신청 방지
// 이미 값이 있다면 (예: 다른 학생 이름이 있다면) 중복 신청으로 간주
if (targetCell.getValue().toString().trim() !== '') {
const existingStudent = targetCell.getValue();
Logger.log(`[중복 신청] ${formData.consultDate} ${formData.consultTime} 시간은 이미 ${existingStudent} 학생이 신청했습니다.`);
throw new Error(`죄송합니다. ${formData.consultDate} ${formData.consultTime} 시간은 이미 신청되었습니다. 다른 시간을 선택해주세요.`);
}
// 셀에 학생 이름 기록
targetCell.setValue(formData.studentName);
// 선택 사항: 상담 내용을 별도의 시트나 로그에 기록
Logger.log('--- 새로운 상담 신청 ---');
Logger.log('학생 이름: ' + formData.studentName);
Logger.log('희망 날짜: ' + formData.consultDate);
Logger.log('희망 시간: ' + formData.consultTime);
Logger.log('상담 내용: ' + formData.consultContent);
Logger.log('------------------------');
// 성공 메시지 반환 (클라이언트의 onSuccess 핸들러로 전달됨)
return '상담 신청이 성공적으로 접수되었습니다.';
}
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<style>
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
min-height: 100vh;
margin: 0;
padding-top: 30px;
padding-bottom: 50px; /* 하단 여백 추가 */
background-color: #f4f4f4;
}
h1 {
color: #333;
margin-bottom: 20px;
}
.greeting-box {
background-color: #e0f7fa;
border: 1px solid #b2ebf2;
border-radius: 8px;
padding: 20px;
margin-bottom: 30px;
text-align: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
max-width: 600px;
width: 90%;
}
.greeting-box p {
margin: 0;
font-size: 1.1em;
color: #00796b;
line-height: 1.5;
}
table {
width: 80%;
max-width: 800px; /* 표 최대 너비 설정 */
border-collapse: collapse;
margin-top: 20px;
background-color: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
th, td {
border: 1px solid #ddd;
padding: 12px 8px;
text-align: center;
}
th {
background-color: #f2f2f2;
font-weight: bold;
color: #333;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
th:first-child, td:first-child {
width: 15%;
}
/* 상담 신청 섹션 스타일 */
.application-section {
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: 30px;
margin-top: 40px;
width: 80%;
max-width: 600px;
}
.application-section h2 {
color: #333;
text-align: center;
margin-bottom: 25px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #555;
}
.form-group input[type="text"],
.form-group select,
.form-group textarea {
width: calc(100% - 20px); /* 패딩 고려 */
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 1em;
box-sizing: border-box; /* 패딩이 너비에 포함되도록 */
}
.form-group input[type="radio"] {
margin-right: 5px;
}
.form-group .radio-option {
margin-right: 20px;
display: inline-block;
}
.form-group textarea {
resize: vertical; /* 세로 크기 조절 가능 */
min-height: 80px;
}
.submit-button {
display: block;
width: 100%;
padding: 12px 20px;
font-size: 1.1em;
cursor: pointer;
background-color: #007bff; /* 파란색 버튼 */
color: white;
border: none;
border-radius: 5px;
transition: background-color 0.3s ease, transform 0.1s ease;
margin-top: 30px;
}
.submit-button:hover {
background-color: #0056b3; /* 호버 시 색상 */
transform: translateY(-2px); /* 약간 위로 떠오르는 효과 */
}
.submit-button:active {
transform: translateY(0); /* 클릭 시 효과 */
}
.submit-button.submitted {
background-color: #28a745; /* 전송 완료 시 초록색 */
cursor: default;
}
</style>
</head>
<body>
<h1>김해제일고 3학년 10반 학부모 상담 신청</h1>
<div class="greeting-box">
<p>안녕하세요. 상담 날짜와 시간을 입력하여 신청해주세요.</p>
</div>
<table>
<thead>
<tr>
<th></th>
<th>월</th>
<th>화</th>
<th>수</th>
<th>목</th>
<th>금</th>
</tr>
</thead>
<tbody>
<tr>
<td>학생명</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>19:00-20:00</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>20:00-21:00</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<div class="application-section">
<h2>상담 신청</h2>
<div class="form-group">
<label for="studentName">1. 학생 이름을 적어주세요</label>
<input type="text" id="studentName" name="studentName" required>
</div>
<div class="form-group">
<label for="consultDate">2. 상담 희망 날짜를 택해주세요</label>
<select id="consultDate" name="consultDate" required>
<option value="">날짜 선택</option>
<option value="9.8.">9.8.</option>
<option value="9.9.">9.9.</option>
<option value="9.10.">9.10.</option>
<option value="9.11.">9.11.</option>
<option value="9.12.">9.12.</option>
</select>
</div>
<div class="form-group">
<label>3. 상담 희망 시간을 택해주세요</label>
<div class="radio-group">
<label class="radio-option">
<input type="radio" name="consultTime" value="19:00-20:00" required> 19:00-20:00
</label>
<label class="radio-option">
<input type="radio" name="consultTime" value="20:00-21:00"> 20:00-21:00
</label>
</div>
</div>
<div class="form-group">
<label for="consultContent">4. 상담 시 상의하실 내용을 간략히 적어주세요</label>
<textarea id="consultContent" name="consultContent" placeholder="30자 이내로만 작성부탁드립니다" maxlength="30"></textarea>
</div>
<button type="button" class="submit-button" id="submitBtn" onclick="submitForm()">전송</button>
<div id="submitMessage" style="text-align: center; color: green; margin-top: 15px;"></div>
</div>
<script>
// 폼 데이터를 Apps Script 백엔드로 전송하는 함수
function submitForm() {
const studentName = document.getElementById('studentName').value;
const consultDate = document.getElementById('consultDate').value;
const consultTime = document.querySelector('input[name="consultTime"]:checked');
const consultContent = document.getElementById('consultContent').value;
const submitBtn = document.getElementById('submitBtn');
const submitMessage = document.getElementById('submitMessage');
// 간단한 유효성 검사
if (!studentName || !consultDate || !consultTime) {
alert('학생 이름, 희망 날짜, 희망 시간을 모두 입력해주세요.');
return;
}
const formData = {
studentName: studentName,
consultDate: consultDate,
consultTime: consultTime.value,
consultContent: consultContent
};
// 전송 버튼 비활성화 및 텍스트 변경
submitBtn.disabled = true;
submitBtn.innerText = '전송 중...';
submitBtn.classList.remove('submitted'); // 혹시 모를 재전송 대비
// Apps Script 함수 호출
google.script.run
.withSuccessHandler(onFormSuccess)
.withFailureHandler(onFormFailure)
.processConsultationRequest(formData); // 백엔드에 보낼 함수
}
function onFormSuccess(response) {
const submitBtn = document.getElementById('submitBtn');
const submitMessage = document.getElementById('submitMessage');
submitBtn.innerText = '전송 완료';
submitBtn.classList.add('submitted'); // 전송 완료 스타일 적용
submitBtn.disabled = false; // 다시 활성화 (새로운 신청을 위해)
// 폼 필드 초기화
document.getElementById('studentName').value = '';
document.getElementById('consultDate').value = '';
const radioButtons = document.querySelectorAll('input[name="consultTime"]');
radioButtons.forEach(radio => radio.checked = false);
document.getElementById('consultContent').value = '';
submitMessage.innerText = response || '상담 신청이 성공적으로 접수되었습니다.';
submitMessage.style.color = 'green';
// 3초 후 메시지 사라지게
setTimeout(() => {
submitMessage.innerText = '';
submitBtn.innerText = '전송'; // 버튼 텍스트 원래대로
submitBtn.classList.remove('submitted');
}, 3000);
}
function onFormFailure(error) {
const submitBtn = document.getElementById('submitBtn');
const submitMessage = document.getElementById('submitMessage');
submitBtn.disabled = false;
submitBtn.innerText = '전송'; // 에러 발생 시 버튼 텍스트 원상 복구
submitBtn.classList.remove('submitted');
submitMessage.innerText = '상담 신청 중 오류가 발생했습니다: ' + error.message;
submitMessage.style.color = 'red';
console.error("Form Submission Error:", error);
}
</script>
</body>
</html>
댓글 남기기