今日のふりかえり(★でひょうか)
Google Apps Script デプロイ用ファイル一式
クラス共有版(児童の回答をGoogleスプレッドシートに集約する機能つき)を
Google Apps Scriptにデプロイするための3つのファイルです。
それぞれの「コピー」ボタンで個別にコピーし、
③ README.txt の手順に沿って script.google.com に貼り付けてください。
① index.html
② Code.gs
③ README.txt(手順)
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>今日のふりかえり(★でひょうか)</title>
<style>
:root { color-scheme: light; }
* { box-sizing: border-box; }
button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible, [tabindex]:focus-visible {
outline: 2px solid #2563eb;
outline-offset: 2px;
}
body {
margin: 0;
font-family: "Hiragino Maru Gothic ProN", "Yu Gothic", -apple-system, sans-serif;
background: #FBF6EC;
color: #2E3440;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 24px;
}
.card {
width: 100%;
max-width: 480px;
background: #ffffff;
border-radius: 22px;
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.08);
padding: 30px 26px;
text-align: center;
}
h1 { font-size: 22px; margin: 0 0 6px; color: #376C96; }
.sub { color: #6B6A62; font-size: 13px; margin-bottom: 26px; }
.question {
margin-bottom: 26px;
text-align: left;
}
.question .q-text {
font-size: 16px;
font-weight: 700;
margin-bottom: 10px;
color: #2E3440;
}
.stars {
display: flex;
gap: 6px;
justify-content: center;
}
.star-btn {
font-size: 40px;
line-height: 1;
background: none;
border: none;
cursor: pointer;
padding: 4px;
color: #D9D2C2;
transition: all 0.15s ease-out;
}
.star-btn.filled { color: #E8B84B; }
.star-btn:active { transform: scale(0.9); }
#saveBtn {
width: 100%;
margin-top: 8px;
padding: 14px;
font-size: 17px;
font-weight: 700;
border: none;
border-radius: 14px;
background: #4F8FBF;
color: white;
cursor: pointer;
box-shadow: 0 4px 0 #376C96;
transition: all 0.15s ease-out;
}
#saveBtn:active { transform: translateY(4px); box-shadow: 0 0 0 #376C96; }
#savedMsg {
margin-top: 14px;
font-size: 14px;
font-weight: 700;
color: #4F8A4B;
min-height: 20px;
}
#send-section{
margin-top: 22px;
padding-top: 18px;
border-top: 1px solid #E5E1D6;
}
#name-row{
display:flex; gap:8px; align-items:center; margin-bottom: 12px;
}
#name-row label{ font-size:13px; font-weight:700; white-space:nowrap; }
#name-input{
flex:1 1 auto;
padding: 10px 12px;
font-size: 14px;
border-radius: 10px;
border: 2px solid #cbd5e1;
font-family: inherit;
}
#name-input:focus{ outline:none; border-color:#2563eb; box-shadow:0 0 0 3px #dbeafe; }
#send-btn{
width:100%;
padding: 13px;
font-size: 15px;
font-weight: 700;
border: none;
border-radius: 12px;
background: #D9634C;
color: #fff;
cursor: pointer;
transition: all 0.15s ease-out;
}
#send-btn:hover{ background: #B5432E; }
#send-btn:disabled{ opacity:0.6; cursor:default; }
#send-status{
margin-top: 10px;
font-size: 13px;
font-weight: 700;
text-align:center;
min-height: 18px;
}
</style>
</head>
<body>
<div class="card">
<h1>今日のふりかえり</h1>
<div class="sub">★をタップして きろくしよう</div>
<div class="question" data-q="q1">
<div class="q-text">① 今日の べんきょうは たのしかった?</div>
<div class="stars" data-for="q1">
<button class="star-btn" data-value="1" type="button">★</button>
<button class="star-btn" data-value="2" type="button">★</button>
<button class="star-btn" data-value="3" type="button">★</button>
<button class="star-btn" data-value="4" type="button">★</button>
<button class="star-btn" data-value="5" type="button">★</button>
</div>
</div>
<div class="question" data-q="q2">
<div class="q-text">② せんせいの はなしを よく きけた?</div>
<div class="stars" data-for="q2">
<button class="star-btn" data-value="1" type="button">★</button>
<button class="star-btn" data-value="2" type="button">★</button>
<button class="star-btn" data-value="3" type="button">★</button>
<button class="star-btn" data-value="4" type="button">★</button>
<button class="star-btn" data-value="5" type="button">★</button>
</div>
</div>
<div class="question" data-q="q3">
<div class="q-text">③ がんばって とりくめた?</div>
<div class="stars" data-for="q3">
<button class="star-btn" data-value="1" type="button">★</button>
<button class="star-btn" data-value="2" type="button">★</button>
<button class="star-btn" data-value="3" type="button">★</button>
<button class="star-btn" data-value="4" type="button">★</button>
<button class="star-btn" data-value="5" type="button">★</button>
</div>
</div>
<button id="saveBtn" type="button">きろくする</button>
<div id="savedMsg"></div>
<div id="send-section">
<div id="name-row">
<label for="name-input">なまえ</label>
<input type="text" id="name-input" placeholder="なまえを入力してね">
</div>
<button id="send-btn" type="button">📤 みんなに送る</button>
<div id="send-status"></div>
</div>
</div>
<script>
const NOTES_KEY = 'reflection-stars-lowgrade';
function safeGet(key){ try{ return localStorage.getItem(key); } catch(e){ return null; } }
function safeSet(key, val){ try{ localStorage.setItem(key, val); } catch(e){} }
const ratings = { q1: 0, q2: 0, q3: 0 };
function applyStars(question, value){
const group = document.querySelector(`.stars[data-for="${question}"]`);
group.querySelectorAll('.star-btn').forEach((btn) => {
const v = Number(btn.dataset.value);
btn.classList.toggle('filled', v <= value);
});
}
document.querySelectorAll('.stars').forEach((group) => {
const question = group.dataset.for;
group.querySelectorAll('.star-btn').forEach((btn) => {
btn.addEventListener('click', () => {
const value = Number(btn.dataset.value);
ratings[question] = value;
applyStars(question, value);
});
});
});
function loadSaved(){
const raw = safeGet(NOTES_KEY);
if(!raw) return;
try{
const data = JSON.parse(raw);
['q1', 'q2', 'q3'].forEach((q) => {
if(data[q]){
ratings[q] = data[q];
applyStars(q, data[q]);
}
});
} catch(e) {}
}
const saveBtn = document.getElementById('saveBtn');
const savedMsg = document.getElementById('savedMsg');
saveBtn.addEventListener('click', () => {
if(ratings.q1 === 0 || ratings.q2 === 0 || ratings.q3 === 0){
savedMsg.textContent = 'すべての しつもんに ★をつけてね';
savedMsg.style.color = '#B4552F';
return;
}
safeSet(NOTES_KEY, JSON.stringify(ratings));
savedMsg.textContent = 'きろくしました!おつかれさま!';
savedMsg.style.color = '#4F8A4B';
});
const nameInput = document.getElementById('name-input');
const sendBtn = document.getElementById('send-btn');
const sendStatus = document.getElementById('send-status');
const NAME_KEY = 'reflection-stars-lowgrade-name';
nameInput.value = safeGet(NAME_KEY) || '';
sendBtn.addEventListener('click', () => {
if(ratings.q1 === 0 || ratings.q2 === 0 || ratings.q3 === 0){
sendStatus.textContent = 'すべての しつもんに ★をつけてね';
sendStatus.style.color = '#dc2626';
return;
}
const name = nameInput.value.trim();
if(!name){
sendStatus.textContent = 'なまえを入力してね';
sendStatus.style.color = '#dc2626';
nameInput.focus();
return;
}
safeSet(NAME_KEY, name);
if (typeof google === 'undefined' || !google.script || !google.script.run) {
sendStatus.textContent = 'この機能はGoogleスプレッドシート連携版として配信された環境でのみ使えます。';
sendStatus.style.color = '#dc2626';
return;
}
sendBtn.disabled = true;
sendStatus.textContent = '送信中…';
sendStatus.style.color = '#64748b';
google.script.run
.withSuccessHandler(function(){
sendStatus.textContent = '送りました!ありがとう!';
sendStatus.style.color = '#16a34a';
sendBtn.disabled = false;
})
.withFailureHandler(function(){
sendStatus.textContent = '送信に失敗しました。もう一度試してね';
sendStatus.style.color = '#dc2626';
sendBtn.disabled = false;
})
.sendReflection({
name: name,
q1: ratings.q1,
q2: ratings.q2,
q3: ratings.q3
});
});
loadSaved();
</script>
</body>
</html>
/**
* 「今日のふりかえり(★でひょうか)」 - みんなの回答をスプレッドシートに送る機能
*
* 使い方(デプロイ手順)は README.txt を参照してください。
*/
function doGet(e) {
return HtmlService.createTemplateFromFile('index')
.evaluate()
.setTitle('今日のふりかえり(★でひょうか)')
.addMetaTag('viewport', 'width=device-width, initial-scale=1')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
function getOrCreateSpreadsheet_() {
const props = PropertiesService.getScriptProperties();
const savedId = props.getProperty('SPREADSHEET_ID');
if (savedId) {
try {
return SpreadsheetApp.openById(savedId);
} catch (err) {
// 保存されていたIDが無効な場合は作り直す
}
}
const ss = SpreadsheetApp.create('今日のふりかえり(★でひょうか) - みんなのふりかえり');
const sheet = ss.getSheets()[0];
sheet.appendRow(['送信日時', 'なまえ', '①たのしかった', '②よくきけた', '③がんばれた']);
sheet.setFrozenRows(1);
props.setProperty('SPREADSHEET_ID', ss.getId());
return ss;
}
/**
* フロント側から google.script.run 経由で呼ばれる。
* data = { name: string, q1: number(1-5), q2: number(1-5), q3: number(1-5) }
*/
function sendReflection(data) {
if (!data || !data.name) {
throw new Error('なまえがありません');
}
const ss = getOrCreateSpreadsheet_();
const sheet = ss.getSheets()[0];
sheet.appendRow([
new Date(),
data.name.toString().slice(0, 40),
data.q1,
data.q2,
data.q3,
]);
return { ok: true };
}
「今日のふりかえり(★でひょうか)」 スプレッドシート送信版 - デプロイ手順
====================================================
このフォルダには3つのファイルがあります。
・index.html … 児童が触る画面(オフライン版に「なまえ」欄と「送る」ボタンを追加したもの)
・Code.gs … スプレッドシートにデータを送るためのバックエンド処理
・README.txt … このファイル
【1】Google Apps Script プロジェクトを作る
1. Google ドライブを開く(学校アカウントでログイン)
2. 「新規」→「その他」→「Google Apps Script」
(見当たらない場合は https://script.google.com/ から「新しいプロジェクト」)
3. プロジェクト名を「今日のふりかえり(★でひょうか)」などにする
【2】コードを貼り付ける
1. 左側のファイル一覧にある「Code.gs」を開き、中身をすべて削除
2. 渡した Code.gs の内容をすべて貼り付けて保存(Ctrl+S)
3. 左側の「+」→「HTML」を選び、ファイル名を必ず「index」にする(拡張子は自動でつきます)
4. 渡した index.html の内容をすべて貼り付けて保存
【3】ウェブアプリとして公開する
1. 右上の「デプロイ」→「新しいデプロイ」
2. 種類の選択(歯車マーク)で「ウェブアプリ」を選ぶ
3. 「次のユーザーとして実行」→ 自分(先生のアカウント)
4. 「アクセスできるユーザー」→ 学校のドメイン内の全員
(例:kita9.ed.jp 内の全員、など。学校のGoogle Workspace設定に合わせてください)
5. 「デプロイ」をクリックし、表示されたウェブアプリのURLをコピーする
6. 初回のみ、権限の承認画面が出るので許可する
【4】児童に配布する
・コピーしたURLをブックマークやGoogle Classroom等で共有すれば、
Chromebookのブラウザからそのまま使えます。
【5】スプレッドシートの確認
・誰かが初めて「送る」を押したときに、自動で
「今日のふりかえり(★でひょうか) - みんなのふりかえり」という
スプレッドシートがドライブに作成されます。
・以降の送信は、そのシートに1人1行ずつ追加されていきます。
・共有設定(誰が見られるか)は、作成後にドライブから
先生が調整してください(デフォルトは先生のみ閲覧可)。
【困ったときは】
・貼り付けたコードを直接書き換えても大丈夫です。次に相談するときは
「Code.gsの◯◯を直したい」のように伝えてもらえれば調整できます。