281 lines
9.6 KiB
HTML
281 lines
9.6 KiB
HTML
<!doctype html>
|
||
<html lang="ja">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>NJTS 会計システム - メイン</title>
|
||
<link rel="stylesheet" href="css/style.css" />
|
||
<link rel="stylesheet" href="/css/mobile.css" />
|
||
<script src="js/auth.js"></script>
|
||
<style>
|
||
body {
|
||
font-family:
|
||
system-ui,
|
||
-apple-system,
|
||
"Yu Gothic UI",
|
||
"Hiragino Kaku Gothic ProN",
|
||
"メイリオ",
|
||
sans-serif;
|
||
padding: 24px;
|
||
}
|
||
.grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 20px;
|
||
max-width: 980px;
|
||
margin: 0 auto;
|
||
}
|
||
.card {
|
||
border: 1px solid #ddd;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
background: #fff;
|
||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.card h2 {
|
||
margin-top: 0;
|
||
}
|
||
.card p:last-child {
|
||
margin-top: auto;
|
||
margin-bottom: 0;
|
||
padding-top: 12px;
|
||
}
|
||
.btn {
|
||
display: inline-block;
|
||
padding: 10px 14px;
|
||
border-radius: 6px;
|
||
background: #007bff;
|
||
color: #fff;
|
||
text-decoration: none;
|
||
white-space: nowrap;
|
||
}
|
||
header {
|
||
max-width: 980px;
|
||
margin: 0 auto 20px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
header h1 {
|
||
margin: 0;
|
||
font-size: 20px;
|
||
}
|
||
.user-info {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 15px;
|
||
font-size: 14px;
|
||
color: #333;
|
||
}
|
||
.logout-btn {
|
||
padding: 8px 12px;
|
||
background: #dc3545;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
}
|
||
.logout-btn:hover {
|
||
background: #c82333;
|
||
}
|
||
.change-pw-btn {
|
||
padding: 8px 12px;
|
||
background: #6c757d;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
}
|
||
.change-pw-btn:hover { background: #545b62; }
|
||
/* パスワード変更モーダル */
|
||
.modal-overlay {
|
||
display: none;
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(0,0,0,0.5);
|
||
z-index: 1000;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
.modal-overlay.active { display: flex; }
|
||
.modal-box {
|
||
background: white;
|
||
border-radius: 10px;
|
||
padding: 32px;
|
||
width: 360px;
|
||
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||
}
|
||
.modal-box h3 { margin: 0 0 20px; }
|
||
.modal-box .form-group { margin-bottom: 14px; }
|
||
.modal-box label { display: block; margin-bottom: 5px; font-size: 13px; font-weight: 600; color: #333; }
|
||
.modal-box input[type="password"] {
|
||
width: 100%; padding: 10px; border: 1px solid #ddd;
|
||
border-radius: 5px; font-size: 14px; box-sizing: border-box;
|
||
}
|
||
.modal-box .btn-row { display: flex; gap: 10px; margin-top: 20px; }
|
||
.modal-box .btn-row button { flex: 1; padding: 10px; border: none; border-radius: 5px; cursor: pointer; font-size: 14px; }
|
||
.modal-box .btn-submit { background: #007bff; color: white; }
|
||
.modal-box .btn-submit:hover { background: #0056b3; }
|
||
.modal-box .btn-cancel { background: #e9ecef; color: #333; }
|
||
.modal-box .btn-cancel:hover { background: #d0d5db; }
|
||
.modal-msg { padding: 8px 12px; border-radius: 4px; margin-bottom: 12px; display: none; font-size: 13px; }
|
||
.modal-msg.error { background: #fee; color: #c33; border: 1px solid #fcc; }
|
||
.modal-msg.success { background: #efe; color: #3a3; border: 1px solid #cfc; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header>
|
||
<h1>NJTS</h1>
|
||
<div class="user-info">
|
||
<span>ユーザー: <strong id="username">読み込み中...</strong></span>
|
||
<button class="change-pw-btn" onclick="openChangePw()">パスワード変更</button>
|
||
<button class="logout-btn" onclick="logout()">ログアウト</button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- パスワード変更モーダル -->
|
||
<div class="modal-overlay" id="changePwModal">
|
||
<div class="modal-box">
|
||
<h3>🔒 パスワード変更</h3>
|
||
<div class="modal-msg" id="changePwMsg"></div>
|
||
<div class="form-group">
|
||
<label>現在のパスワード</label>
|
||
<input type="password" id="cpCurrent" placeholder="現在のパスワード" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label>新しいパスワード(8文字以上)</label>
|
||
<input type="password" id="cpNew" placeholder="新しいパスワード" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label>新しいパスワード(確認)</label>
|
||
<input type="password" id="cpConfirm" placeholder="もう一度入力" />
|
||
</div>
|
||
<div class="btn-row">
|
||
<button class="btn-submit" onclick="submitChangePw()">変更する</button>
|
||
<button class="btn-cancel" onclick="closeChangePw()">キャンセル</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<main class="grid">
|
||
<section class="card">
|
||
<h2>会計システム</h2>
|
||
<p>
|
||
会計モジュールの主要画面へ移動します。仕訳入力、試算表、元帳に移動できます。
|
||
</p>
|
||
<p>
|
||
<a class="btn" href="journal-entry.html">仕訳入力へ</a>
|
||
<a
|
||
class="btn"
|
||
href="trial-balance.html"
|
||
style="margin-left: 10px; background: #28a745"
|
||
>試算表へ</a
|
||
>
|
||
<a
|
||
class="btn"
|
||
href="opening_balance.html"
|
||
style="margin-left: 10px; background: #fd7e14"
|
||
>期首残高入力へ</a
|
||
>
|
||
</p>
|
||
</section>
|
||
|
||
<section class="card">
|
||
<h2>給与システム</h2>
|
||
<p>給与・賞与の計算や従業員管理を行います。</p>
|
||
<p><a class="btn" href="payroll.html">給与システムを開く</a></p>
|
||
</section>
|
||
|
||
<section class="card">
|
||
<h2>🏛 e-Gov 電子送達</h2>
|
||
<p>
|
||
年金事務所からの電子送達書類(社会保険通知など)を取得・管理します。<br />
|
||
GビズIDでe-Govにログインし、毎月の電子通知CSVを確認できます。
|
||
</p>
|
||
<p>
|
||
<a class="btn" href="egov.html" style="background: #6f42c1"
|
||
>e-Gov 連携を開く</a
|
||
>
|
||
</p>
|
||
</section>
|
||
</main>
|
||
|
||
<script>
|
||
// ユーザー情報を表示
|
||
document.addEventListener("DOMContentLoaded", function () {
|
||
const user = getCurrentUser();
|
||
if (user) {
|
||
document.getElementById("username").textContent = user.username;
|
||
}
|
||
});
|
||
|
||
// パスワード変更モーダル
|
||
function openChangePw() {
|
||
document.getElementById("cpCurrent").value = "";
|
||
document.getElementById("cpNew").value = "";
|
||
document.getElementById("cpConfirm").value = "";
|
||
const msg = document.getElementById("changePwMsg");
|
||
msg.style.display = "none";
|
||
document.getElementById("changePwModal").classList.add("active");
|
||
}
|
||
function closeChangePw() {
|
||
document.getElementById("changePwModal").classList.remove("active");
|
||
}
|
||
function showChangePwMsg(text, isError) {
|
||
const msg = document.getElementById("changePwMsg");
|
||
msg.textContent = text;
|
||
msg.className = "modal-msg " + (isError ? "error" : "success");
|
||
msg.style.display = "block";
|
||
}
|
||
async function submitChangePw() {
|
||
const current = document.getElementById("cpCurrent").value;
|
||
const newPw = document.getElementById("cpNew").value;
|
||
const confirm = document.getElementById("cpConfirm").value;
|
||
const user = getCurrentUser();
|
||
if (!current || !newPw || !confirm) {
|
||
showChangePwMsg("すべての項目を入力してください", true); return;
|
||
}
|
||
if (newPw.length < 8) {
|
||
showChangePwMsg("新しいパスワードは8文字以上にしてください", true); return;
|
||
}
|
||
if (newPw !== confirm) {
|
||
showChangePwMsg("新しいパスワードが一致しません", true); return;
|
||
}
|
||
try {
|
||
const res = await fetch("/api/users/change-password", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ username: user.username, current_password: current, new_password: newPw })
|
||
});
|
||
const data = await res.json();
|
||
if (res.ok) {
|
||
showChangePwMsg("パスワードを変更しました。再ログインしてください", false);
|
||
setTimeout(() => { logout(); }, 2000);
|
||
} else {
|
||
showChangePwMsg(data.detail || "変更に失敗しました", true);
|
||
}
|
||
} catch (e) {
|
||
showChangePwMsg("エラー: " + e.message, true);
|
||
}
|
||
}
|
||
|
||
// dropdown toggle
|
||
const toggle = document.getElementById("accountingToggle");
|
||
const menu = document.getElementById("accountingMenu");
|
||
toggle.addEventListener("click", (e) => {
|
||
menu.style.display = menu.style.display === "block" ? "none" : "block";
|
||
});
|
||
// click outside to close
|
||
document.addEventListener("click", (e) => {
|
||
if (!toggle.contains(e.target) && !menu.contains(e.target)) {
|
||
menu.style.display = "none";
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|