「打刻」「各種申請(休日、残業、遅刻、休日出勤)」「設定(ログアウト)」の3つのタブで構成していく。
Contents
出退勤ページ機能拡張
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>出退勤管理</title>
<script src="https://www.gstatic.com/firebasejs/10.11.0/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.11.0/firebase-auth-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.11.0/firebase-firestore-compat.js"></script>
<style>
.tab { display: none; }
.active { display: block; }
.section-content { display: none; margin: 10px 0; padding: 10px; border: 1px solid #ccc; }
</style>
</head>
<body>
<h1>出退勤管理アプリ</h1>
<nav>
<button onclick="showTab('tab-clock')">打刻</button>
<button onclick="showTab('tab-application')">申請</button>
<button onclick="showTab('tab-settings')">設定</button>
</nav>
<!-- 打刻タブ -->
<div id="tab-clock" class="tab active">
<h2>打刻</h2>
<button onclick="recordTime('出勤')">出勤</button>
<button onclick="recordTime('退勤')">退勤</button>
<p id="currentTime"></p>
<p id="result"></p>
</div>
<!-- 申請タブ -->
<div id="tab-application" class="tab">
<h2>申請</h2>
<button onclick="toggleSection('holidaySection')">休日申請</button>
<div id="holidaySection" class="section-content">
<label>日付: <input type="date" id="holidayDate"></label><br>
<label><input type="radio" name="holidayType" value="終日" checked>終日</label>
<label><input type="radio" name="holidayType" value="午前休">午前休</label>
<label><input type="radio" name="holidayType" value="午後休">午後休</label><br>
<button onclick="submitHoliday()">休日申請</button>
</div>
<button onclick="toggleSection('lateSection')">遅刻申請</button>
<div id="lateSection" class="section-content">
<label>日付: <input type="date" id="lateDate"></label><br>
<label>就業開始時間: <input type="time" id="scheduledStart"></label><br>
<label>遅刻時間: <input type="time" id="lateTime"></label><br>
<button onclick="submitLate()">遅刻申請</button>
</div>
<button onclick="toggleSection('overtimeSection')">残業申請</button>
<div id="overtimeSection" class="section-content">
<label>日付: <input type="date" id="overtimeDate"></label><br>
<label>開始時刻: <input type="time" id="overtimeStart"></label>
<label>終了時刻: <input type="time" id="overtimeEnd"></label><br>
<p>理由:</p>
<label><input type="checkbox" name="overtimeReason" value="時間内困難">時間内困難</label>
<label><input type="checkbox" name="overtimeReason" value="突発対応">突発対応</label>
<label><input type="checkbox" name="overtimeReason" value="報告書">報告書</label><br>
<label>その他の理由:<br>
<textarea id="overtimeOtherReason" rows="2" cols="30" placeholder="その他の理由を入力"></textarea>
</label><br>
<button onclick="submitOvertime()">残業申請</button>
</div>
<button onclick="toggleSection('workOnHolidaySection')">休日出勤申請</button>
<div id="workOnHolidaySection" class="section-content">
<label>出勤日: <input type="date" id="workOnHolidayDate"></label><br>
<label>開始時間: <input type="time" id="workOnHolidayStart"></label>
<label>終了時間: <input type="time" id="workOnHolidayEnd"></label><br>
<label>業務内容:<br>
<textarea id="workOnHolidayTask" rows="2" cols="30" placeholder="例: 定期システムメンテナンスなど"></textarea>
</label><br>
<button onclick="submitWorkOnHoliday()">休日出勤申請</button>
</div>
<p id="requestResult"></p>
</div>
<!-- 設定タブ -->
<div id="tab-settings" class="tab">
<h2>設定</h2>
<button onclick="auth.signOut().then(() => alert('ログアウトしました'))">ログアウト</button>
</div>
<script>
const firebaseConfig = {
apiKey: "あなたのAPIキー",
authDomain: "あなたのプロジェクトID.firebaseapp.com",
projectId: "あなたのプロジェクトID",
storageBucket: "あなたのプロジェクトID.appspot.com",
messagingSenderId: "送信者ID",
appId: "アプリID"
};
firebase.initializeApp(firebaseConfig);
const auth = firebase.auth();
const db = firebase.firestore();
function showTab(tabId) {
document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
document.getElementById(tabId).classList.add('active');
}
function toggleSection(id) {
const section = document.getElementById(id);
section.style.display = section.style.display === 'block' ? 'none' : 'block';
}
function updateTime() {
const now = new Date();
document.getElementById('currentTime').innerText = `現在時刻: ${now.toLocaleString()}`;
}
setInterval(updateTime, 1000);
function recordTime(type) {
const user = auth.currentUser;
if (!user) {
document.getElementById("result").innerText = "ログインしていません。";
return;
}
const now = new Date();
db.collection("attendance").add({
uid: user.uid,
type: type,
timestamp: firebase.firestore.Timestamp.fromDate(now)
}).then(() => {
document.getElementById("result").innerText = `${type}打刻しました (${now.toLocaleString()})`;
}).catch((error) => {
document.getElementById("result").innerText = `エラー: ${error.message}`;
});
}
function submitHoliday() {
const user = auth.currentUser;
const date = document.getElementById("holidayDate").value;
const type = document.querySelector('input[name="holidayType"]:checked').value;
const now = new Date();
if (!user || !date || !type) {
document.getElementById("requestResult").innerText = "休日申請の入力に不足があります。";
return;
}
db.collection("applications").add({
uid: user.uid,
type: "休日申請",
date: date,
holidayType: type,
requestedAt: firebase.firestore.Timestamp.fromDate(now),
status: "申請中"
}).then(() => {
document.getElementById("requestResult").innerText = `休日申請を送信しました(${date} - ${type})`;
});
}
function submitLate() {
const user = auth.currentUser;
const date = document.getElementById("lateDate").value;
const scheduledStart = document.getElementById("scheduledStart").value;
const lateTime = document.getElementById("lateTime").value;
const now = new Date();
if (!user || !date || !scheduledStart || !lateTime) {
document.getElementById("requestResult").innerText = "遅刻申請の入力に不足があります。";
return;
}
db.collection("applications").add({
uid: user.uid,
type: "遅刻申請",
date: date,
scheduledStart: scheduledStart,
lateTime: lateTime,
requestedAt: firebase.firestore.Timestamp.fromDate(now),
status: "申請中"
}).then(() => {
document.getElementById("requestResult").innerText = `遅刻申請を送信しました(${date} ${scheduledStart}→${lateTime})`;
});
}
function submitOvertime() {
const user = auth.currentUser;
const date = document.getElementById("overtimeDate").value;
const start = document.getElementById("overtimeStart").value;
const end = document.getElementById("overtimeEnd").value;
const reasons = Array.from(document.querySelectorAll('input[name="overtimeReason"]:checked')).map(el => el.value);
const otherReason = document.getElementById("overtimeOtherReason").value;
const now = new Date();
if (!user || !date || !start || !end) {
document.getElementById("requestResult").innerText = "残業申請の入力に不足があります。";
return;
}
db.collection("applications").add({
uid: user.uid,
type: "残業申請",
date: date,
startTime: start,
endTime: end,
reasons: reasons,
otherReason: otherReason,
requestedAt: firebase.firestore.Timestamp.fromDate(now),
status: "申請中"
}).then(() => {
document.getElementById("requestResult").innerText = `残業申請を送信しました(${date} ${start}~${end})`;
});
}
function submitWorkOnHoliday() {
const user = auth.currentUser;
const date = document.getElementById("workOnHolidayDate").value;
const start = document.getElementById("workOnHolidayStart").value;
const end = document.getElementById("workOnHolidayEnd").value;
const task = document.getElementById("workOnHolidayTask").value;
const now = new Date();
if (!user || !date || !start || !end || !task) {
document.getElementById("requestResult").innerText = "休日出勤申請の入力に不足があります。";
return;
}
db.collection("applications").add({
uid: user.uid,
type: "休日出勤申請",
date: date,
startTime: start,
endTime: end,
task: task,
requestedAt: firebase.firestore.Timestamp.fromDate(now),
status: "申請中"
}).then(() => {
document.getElementById("requestResult").innerText = `休日出勤申請を送信しました(${date} ${start}~${end})`;
});
}
</script>
</body>
</html>
🧱 構造の全体概要
以下に構成と主要機能をわかりやすく解説します。
機能 | 内容 |
---|---|
打刻タブ | 出勤・退勤ボタン、現在時刻の表示 |
申請タブ | 各種申請(休日、遅刻、残業、休日出勤)をボタンで表示・非表示切替可能 |
設定タブ | Firebase認証によるログアウト機能 |
Firebase連携 | 認証(firebase.auth() )と Firestore(firebase.firestore() )利用 |
JavaScript機能 | タブ切替・現在時刻更新・各申請内容送信 |
🔁 タブ機能の解説
<nav>
<button onclick="showTab('tab-clock')">打刻</button>
<button onclick="showTab('tab-application')">申請</button>
<button onclick="showTab('tab-settings')">設定</button>
</nav>
showTab(tabId)
関数で、押したボタンに対応する<div id="tab-xxx">
のみを表示。class="tab active"
で表示中のタブを切り替えます。
⏰ 打刻タブの中身
<button onclick="recordTime('出勤')">出勤</button>
<button onclick="recordTime('退勤')">退勤</button>
<p id="currentTime"></p>
- ボタンで出勤/退勤処理(※
recordTime()
の中身は未定義。追加が必要)。 setInterval(updateTime, 1000)
により、1秒ごとに現在日時を更新し#currentTime
に表示。
📄 申請タブ:申請の種類ごとにボタンで表示切り替え
共通点
- 各申請には「ボタン」「入力フォーム」「送信ボタン」「処理関数」があります。
toggleSection(id)
関数で表示/非表示を切り替えます。
🗓 1. 休日申請
<input type="date" id="holidayDate">
<input type="radio" name="holidayType" value="終日">
...
<button onclick="submitHoliday()">休日申請</button>
- 日付選択と「終日・午前休・午後休」から選択
submitHoliday()
関数は未定義(追加が必要)
⏱ 2. 遅刻申請
<input type="date" id="lateDate">
<input type="time" id="scheduledStart">
<input type="time" id="lateTime">
<button onclick="submitLate()">遅刻申請</button>
- 遅刻の 予定出勤時刻と 実際の出勤時刻 を入力
submitLate()
関数によりFirestoreへ申請内容を保存
db.collection("applications").add({
uid: user.uid,
type: "遅刻申請",
date: date,
scheduledStart: scheduledStart,
lateTime: lateTime,
requestedAt: firebase.firestore.Timestamp.fromDate(now),
status: "申請中"
})
⏰ 3. 残業申請
<input type="date" id="overtimeDate">
<input type="time" id="overtimeStart">
<input type="time" id="overtimeEnd">
- 残業の 日付・時間範囲を入力
- 理由は複数のチェックボックスと自由入力欄
submitOvertime()
関数は未定義(追加が必要)
💼 4. 休日出勤申請
<input type="date" id="workOnHolidayDate">
<input type="time" id="workOnHolidayStart">
<input type="time" id="workOnHolidayEnd">
<textarea id="workOnHolidayTask">
- 日付・勤務時間帯・業務内容を入力
submitWorkOnHoliday()
関数は未定義(追加が必要)
⚙️ 設定タブ
<button onclick="auth.signOut().then(() => alert('ログアウトしました'))">ログアウト</button>
- Firebase Authentication のログアウト処理を実行