Files
njts-accounting-core/backend/app/static/payroll-employees.html
2026-01-20 11:15:49 +09:00

303 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>給与管理 - 従業員管理</title>
<link rel="stylesheet" href="/css/style.css">
<style>
.nav-tabs {
display: flex;
gap: 10px;
margin-bottom: 20px;
border-bottom: 2px solid #ddd;
}
.nav-tab {
padding: 10px 20px;
cursor: pointer;
border: none;
background: #f0f0f0;
border-radius: 5px 5px 0 0;
}
.nav-tab.active {
background: #007bff;
color: white;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.employee-list {
margin-top: 20px;
}
.employee-item {
padding: 15px;
border: 1px solid #ddd;
margin-bottom: 10px;
border-radius: 5px;
cursor: pointer;
}
.employee-item:hover {
background: #f9f9f9;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-group input, .form-group select, .form-group textarea {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.btn {
padding: 10px 20px;
margin-right: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-primary {
background: #007bff;
color: white;
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn-danger {
background: #dc3545;
color: white;
}
.dependent-list {
margin-top: 20px;
padding: 15px;
background: #f9f9f9;
border-radius: 5px;
}
.dependent-item {
padding: 10px;
border: 1px solid #ddd;
margin-bottom: 10px;
background: white;
border-radius: 4px;
}
</style>
</head>
<body>
<div class="container">
<h1>給与管理 - 従業員管理</h1>
<div class="nav-tabs">
<button class="nav-tab active" onclick="showTab('list')">従業員一覧</button>
<button class="nav-tab" onclick="showTab('create')">新規登録</button>
<button class="nav-tab" onclick="showTab('detail')" id="detailTab" style="display:none;">従業員詳細</button>
</div>
<!-- 従業員一覧タブ -->
<div id="tab-list" class="tab-content active">
<div class="filters">
<label>
<input type="checkbox" id="filterActive" checked onchange="loadEmployees()">
在職中のみ表示
</label>
</div>
<div id="employeeList" class="employee-list"></div>
</div>
<!-- 新規登録タブ -->
<div id="tab-create" class="tab-content">
<h2>従業員の新規登録</h2>
<form id="createForm" onsubmit="createEmployee(event)">
<div class="form-group">
<label>従業員コード *</label>
<input type="text" name="employee_code" required>
</div>
<div class="form-group">
<label>氏名 *</label>
<input type="text" name="name" required>
</div>
<div class="form-group">
<label>フリガナ</label>
<input type="text" name="name_kana">
</div>
<div class="form-group">
<label>メールアドレス</label>
<input type="email" name="email">
</div>
<div class="form-group">
<label>電話番号</label>
<input type="tel" name="phone">
</div>
<div class="form-group">
<label>入社日 *</label>
<input type="date" name="hire_date" required>
</div>
<div class="form-group">
<label>銀行名</label>
<input type="text" name="bank_name">
</div>
<div class="form-group">
<label>支店名</label>
<input type="text" name="bank_branch">
</div>
<div class="form-group">
<label>口座番号</label>
<input type="text" name="bank_account_number">
</div>
<div class="form-group">
<label>口座種別</label>
<select name="bank_account_type">
<option value="">選択してください</option>
<option value="普通">普通</option>
<option value="当座">当座</option>
</select>
</div>
<div class="form-group">
<label>備考</label>
<textarea name="notes" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-primary">登録</button>
<button type="button" class="btn btn-secondary" onclick="showTab('list')">キャンセル</button>
</form>
</div>
<!-- 従業員詳細タブ -->
<div id="tab-detail" class="tab-content">
<div id="employeeDetail"></div>
</div>
</div>
<script>
const API_BASE = 'http://localhost:8000';
let currentEmployee = null;
function showTab(tabName) {
document.querySelectorAll('.nav-tab').forEach(tab => tab.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
if (tabName === 'list') {
document.querySelector('.nav-tab').classList.add('active');
document.getElementById('tab-list').classList.add('active');
loadEmployees();
} else if (tabName === 'create') {
document.querySelectorAll('.nav-tab')[1].classList.add('active');
document.getElementById('tab-create').classList.add('active');
} else if (tabName === 'detail') {
document.getElementById('detailTab').classList.add('active');
document.getElementById('tab-detail').classList.add('active');
}
}
async function loadEmployees() {
const isActive = document.getElementById('filterActive').checked;
const url = `${API_BASE}/payroll/employees/${isActive ? '?is_active=true' : ''}`;
try {
const response = await fetch(url);
const employees = await response.json();
const html = employees.map(emp => `
<div class="employee-item" onclick="viewEmployee(${emp.employee_id})">
<strong>${emp.employee_code}</strong> - ${emp.name}
${emp.name_kana ? `(${emp.name_kana})` : ''}
<br>
<small>入社日: ${emp.hire_date} ${emp.termination_date ? ` | 退職日: ${emp.termination_date}` : ''}</small>
</div>
`).join('');
document.getElementById('employeeList').innerHTML = html || '<p>従業員が登録されていません</p>';
} catch (error) {
alert('従業員一覧の取得に失敗しました');
console.error(error);
}
}
async function createEmployee(event) {
event.preventDefault();
const form = event.target;
const formData = new FormData(form);
const data = Object.fromEntries(formData.entries());
// 空の値を除外
Object.keys(data).forEach(key => {
if (data[key] === '') {
delete data[key];
}
});
try {
const response = await fetch(`${API_BASE}/payroll/employees/`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
if (response.ok) {
alert('従業員を登録しました');
form.reset();
showTab('list');
} else {
const error = await response.json();
alert('登録に失敗しました: ' + error.detail);
}
} catch (error) {
alert('登録に失敗しました');
console.error(error);
}
}
async function viewEmployee(employeeId) {
try {
const response = await fetch(`${API_BASE}/payroll/employees/${employeeId}`);
const employee = await response.json();
currentEmployee = employee;
const html = `
<h2>${employee.name} (${employee.employee_code})</h2>
<p><strong>フリガナ:</strong> ${employee.name_kana || '-'}</p>
<p><strong>メール:</strong> ${employee.email || '-'}</p>
<p><strong>電話:</strong> ${employee.phone || '-'}</p>
<p><strong>入社日:</strong> ${employee.hire_date}</p>
<p><strong>退職日:</strong> ${employee.termination_date || '-'}</p>
<p><strong>銀行:</strong> ${employee.bank_name || '-'} ${employee.bank_branch || ''}</p>
<p><strong>口座:</strong> ${employee.bank_account_type || ''} ${employee.bank_account_number || '-'}</p>
<p><strong>備考:</strong> ${employee.notes || '-'}</p>
<div class="dependent-list">
<h3>扶養家族</h3>
${employee.dependents.map(dep => `
<div class="dependent-item">
<strong>${dep.name}</strong> (${dep.relationship})
${dep.birth_date ? ` - 生年月日: ${dep.birth_date}` : ''}
${dep.is_spouse ? ' <span style="color:blue;">[配偶者]</span>' : ''}
${dep.is_disabled ? ' <span style="color:red;">[障害者]</span>' : ''}
<br><small>有効期間: ${dep.valid_from} ${dep.valid_to || '現在'}</small>
</div>
`).join('') || '<p>扶養家族が登録されていません</p>'}
</div>
<button class="btn btn-secondary" onclick="showTab('list')">戻る</button>
`;
document.getElementById('employeeDetail').innerHTML = html;
document.getElementById('detailTab').style.display = 'block';
showTab('detail');
} catch (error) {
alert('従業員情報の取得に失敗しました');
console.error(error);
}
}
// 初期化
loadEmployees();
</script>
</body>
</html>