This commit is contained in:
admin
2026-02-05 16:47:38 +09:00
parent 7e67f9d422
commit 9d1ad5594d

View File

@@ -1,104 +1,228 @@
<!DOCTYPE html> <!doctype html>
<html lang="ja"> <html lang="ja">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>給与管理 - 従業員管理</title> <title>給与管理 - 従業員管理</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="/css/style.css" /> <link rel="stylesheet" href="/css/style.css" />
<style> <style>
body {
font-family: "Noto Sans JP", Arial, sans-serif;
background: #f6f8fa;
color: #222;
margin: 0;
}
.container {
max-width: 900px;
margin: 30px auto 40px auto;
background: #fff;
border-radius: 16px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
padding: 32px 24px 24px 24px;
}
h1 {
font-size: 2.1rem;
font-weight: 700;
margin-bottom: 24px;
letter-spacing: 0.02em;
color: #007bff;
text-align: center;
}
h2,
h3 {
font-weight: 700;
color: #0056b3;
margin-top: 24px;
margin-bottom: 12px;
}
.nav-tabs { .nav-tabs {
display: flex; display: flex;
gap: 10px; gap: 10px;
margin-bottom: 20px; margin-bottom: 24px;
border-bottom: 2px solid #ddd; border-bottom: 2px solid #e3e7ee;
justify-content: center;
} }
.nav-tab { .nav-tab {
padding: 10px 20px; padding: 12px 32px;
cursor: pointer; cursor: pointer;
border: none; border: none;
background: #f0f0f0; background: #e9f2ff;
border-radius: 5px 5px 0 0; border-radius: 8px 8px 0 0;
font-size: 1.05rem;
font-weight: 500;
color: #007bff;
transition:
background 0.2s,
color 0.2s;
} }
.nav-tab.active { .nav-tab.active {
background: #007bff; background: #007bff;
color: white; color: #fff;
box-shadow: 0 2px 8px rgba(0, 123, 255, 0.08);
}
.nav-tab:not(.active):hover {
background: #d0e6ff;
color: #0056b3;
} }
.tab-content { .tab-content {
display: none; display: none;
animation: fadeIn 0.3s;
} }
.tab-content.active { .tab-content.active {
display: block; display: block;
} }
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.employee-list { .employee-list {
margin-top: 20px; margin-top: 20px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 18px;
} }
.employee-item { .employee-item {
padding: 15px; background: #fff;
border: 1px solid #ddd; padding: 18px 16px;
margin-bottom: 10px; border: 1px solid #e3e7ee;
border-radius: 5px; border-radius: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
cursor: pointer; cursor: pointer;
transition:
box-shadow 0.2s,
border 0.2s,
background 0.2s;
} }
.employee-item:hover { .employee-item:hover {
background: #f9f9f9; background: #f0f8ff;
border-color: #007bff;
box-shadow: 0 4px 16px rgba(0, 123, 255, 0.08);
} }
.form-group { .form-group {
margin-bottom: 15px; margin-bottom: 18px;
} }
.form-group label { .form-group label {
display: block; display: block;
margin-bottom: 5px; margin-bottom: 6px;
font-weight: bold; font-weight: 700;
color: #0056b3;
} }
.form-group input, .form-group input,
.form-group select, .form-group select,
.form-group textarea { .form-group textarea {
width: 100%; width: 100%;
padding: 8px; padding: 10px;
border: 1px solid #ddd; border: 1px solid #e3e7ee;
border-radius: 4px; border-radius: 6px;
font-size: 1rem;
background: #f8fbff;
transition: border 0.2s;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
border-color: #007bff;
outline: none;
background: #fff;
} }
.btn { .btn {
padding: 10px 20px; padding: 10px 24px;
margin-right: 10px; margin-right: 10px;
border: none; border: none;
border-radius: 4px; border-radius: 6px;
cursor: pointer; cursor: pointer;
font-size: 1rem;
font-weight: 500;
transition:
background 0.2s,
color 0.2s,
box-shadow 0.2s;
box-shadow: 0 2px 8px rgba(0, 123, 255, 0.06);
} }
.btn-primary { .btn-primary {
background: #007bff; background: linear-gradient(90deg, #007bff 0%, #0056b3 100%);
color: white; color: #fff;
}
.btn-primary:hover {
background: linear-gradient(90deg, #0056b3 0%, #007bff 100%);
color: #fff;
box-shadow: 0 4px 16px rgba(0, 123, 255, 0.12);
} }
.btn-secondary { .btn-secondary {
background: #6c757d; background: #e3e7ee;
color: white; color: #007bff;
}
.btn-secondary:hover {
background: #d0e6ff;
color: #0056b3;
} }
.btn-danger { .btn-danger {
background: #dc3545; background: #dc3545;
color: white; color: #fff;
}
.btn-danger:hover {
background: #b71c1c;
color: #fff;
}
.filters {
margin-bottom: 18px;
text-align: right;
} }
.dependent-list { .dependent-list {
margin-top: 20px; margin-top: 24px;
padding: 15px; padding: 18px;
background: #f9f9f9; background: #f8fbff;
border-radius: 5px; border-radius: 10px;
box-shadow: 0 2px 8px rgba(0, 123, 255, 0.04);
} }
.dependent-item { .dependent-item {
padding: 10px; padding: 12px 10px;
border: 1px solid #ddd; border: 1px solid #e3e7ee;
margin-bottom: 10px; margin-bottom: 10px;
background: white; background: #fff;
border-radius: 4px; border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
}
.dependent-item strong {
color: #007bff;
} }
.back-link { .back-link {
display: inline-block; display: inline-block;
margin-bottom: 20px; margin-bottom: 24px;
color: #007bff; color: #007bff;
text-decoration: none; text-decoration: none;
font-size: 14px; font-size: 15px;
font-weight: 500;
transition: color 0.2s;
} }
.back-link:hover { .back-link:hover {
text-decoration: underline; text-decoration: underline;
color: #0056b3;
}
@media (max-width: 600px) {
.container {
padding: 12px 4px;
}
h1 {
font-size: 1.3rem;
}
.nav-tabs {
flex-direction: column;
gap: 4px;
}
.employee-list {
grid-template-columns: 1fr;
}
} }
</style> </style>
</head> </head>
@@ -597,7 +721,7 @@
async function uploadFileToServer( async function uploadFileToServer(
file, file,
employeeCode, employeeCode,
fileType = "image" fileType = "image",
) { ) {
const formData = new FormData(); const formData = new FormData();
formData.append("file", file); formData.append("file", file);
@@ -609,7 +733,7 @@
{ {
method: "POST", method: "POST",
body: formData, body: formData,
} },
); );
if (!response.ok) { if (!response.ok) {
@@ -676,10 +800,12 @@
${emp.name_kana ? `(${emp.name_kana})` : ""} ${emp.name_kana ? `(${emp.name_kana})` : ""}
<br> <br>
<small>入社日: ${emp.hire_date} ${ <small>入社日: ${emp.hire_date} ${
emp.termination_date ? ` | 退職日: ${emp.termination_date}` : "" emp.termination_date
}</small> ? ` | 退職日: ${emp.termination_date}`
: ""
}</small>
</div> </div>
` `,
) )
.join(""); .join("");
@@ -719,7 +845,7 @@
const filePath = await uploadFileToServer( const filePath = await uploadFileToServer(
fileInput.files[0], fileInput.files[0],
employeeCode, employeeCode,
"image" "image",
); );
photoUploads[field] = filePath; photoUploads[field] = filePath;
} catch (error) { } catch (error) {
@@ -783,7 +909,7 @@
async function viewEmployee(employeeId) { async function viewEmployee(employeeId) {
try { try {
const response = await fetch( const response = await fetch(
`${API_BASE}/payroll/employees/${employeeId}` `${API_BASE}/payroll/employees/${employeeId}`,
); );
const employee = await response.json(); const employee = await response.json();
currentEmployee = employee; currentEmployee = employee;
@@ -796,19 +922,19 @@
<p><strong>生年月日:</strong> ${ <p><strong>生年月日:</strong> ${
employee.birth_date || "-" employee.birth_date || "-"
} ${ } ${
employee.birth_date employee.birth_date
? `(${calculateAge(employee.birth_date)}歳)` ? `(${calculateAge(employee.birth_date)}歳)`
: "" : ""
}</p> }</p>
<p><strong>性別:</strong> ${employee.gender || "-"}</p> <p><strong>性別:</strong> ${employee.gender || "-"}</p>
<p><strong>マイナンバー:</strong> ${ <p><strong>マイナンバー:</strong> ${
employee.my_number employee.my_number
? `${employee.my_number.substring( ? `${employee.my_number.substring(
0, 0,
4 4,
)}-${employee.my_number.substring( )}-${employee.my_number.substring(
4, 4,
8 8,
)}-${employee.my_number.substring(8, 12)}` )}-${employee.my_number.substring(8, 12)}`
: "-" : "-"
}</p> }</p>
@@ -823,8 +949,8 @@
employee.termination_date || "-" employee.termination_date || "-"
}</p> }</p>
<p><strong>銀行:</strong> ${employee.bank_name || "-"} ${ <p><strong>銀行:</strong> ${employee.bank_name || "-"} ${
employee.bank_branch || "" employee.bank_branch || ""
}</p> }</p>
<p><strong>口座:</strong> ${ <p><strong>口座:</strong> ${
employee.bank_account_type || "" employee.bank_account_type || ""
} ${employee.bank_account_number || "-"}</p> } ${employee.bank_account_number || "-"}</p>
@@ -853,7 +979,7 @@
<strong>マイナンバーカード</strong><br> <strong>マイナンバーカード</strong><br>
<img src="${ <img src="${
employee.my_number_card_image_url.startsWith( employee.my_number_card_image_url.startsWith(
"data:" "data:",
) )
? employee.my_number_card_image_url ? employee.my_number_card_image_url
: API_BASE + employee.my_number_card_image_url : API_BASE + employee.my_number_card_image_url
@@ -869,7 +995,7 @@
<strong>在留カード</strong><br> <strong>在留カード</strong><br>
<img src="${ <img src="${
employee.residence_card_image_url.startsWith( employee.residence_card_image_url.startsWith(
"data:" "data:",
) )
? employee.residence_card_image_url ? employee.residence_card_image_url
: API_BASE + employee.residence_card_image_url : API_BASE + employee.residence_card_image_url
@@ -885,7 +1011,7 @@
<strong>健康保険証</strong><br> <strong>健康保険証</strong><br>
<img src="${ <img src="${
employee.health_insurance_card_image_url.startsWith( employee.health_insurance_card_image_url.startsWith(
"data:" "data:",
) )
? employee.health_insurance_card_image_url ? employee.health_insurance_card_image_url
: API_BASE + : API_BASE +
@@ -924,7 +1050,7 @@
) { ) {
koureiCount = employee.dependents.filter((dep) => { koureiCount = employee.dependents.filter((dep) => {
const taxStatus = getTaxDeductionStatus( const taxStatus = getTaxDeductionStatus(
dep.birth_date dep.birth_date,
); );
// 源泉税控除対象は16歳以上一般扶養親族16-18歳、特定扶養親族19-22歳、老人扶養親族70歳以上 // 源泉税控除対象は16歳以上一般扶養親族16-18歳、特定扶養親族19-22歳、老人扶養親族70歳以上
return ( return (
@@ -950,13 +1076,13 @@
? employee.dependents ? employee.dependents
.map((dep) => { .map((dep) => {
const taxStatus = getTaxDeductionStatus( const taxStatus = getTaxDeductionStatus(
dep.birth_date dep.birth_date,
); );
return ` return `
<div class="dependent-item"> <div class="dependent-item">
<strong>${dep.name}</strong> (${ <strong>${dep.name}</strong> (${
dep.relationship dep.relationship
}) })
${ ${
dep.birth_date dep.birth_date
? ` - 生年月日: ${dep.birth_date} (${taxStatus?.age}歳)` ? ` - 生年月日: ${dep.birth_date} (${taxStatus?.age}歳)`
@@ -978,10 +1104,10 @@
: "" : ""
} }
<br><small>有効期間: ${dep.valid_from} ${ <br><small>有効期間: ${dep.valid_from} ${
dep.valid_to || "現在" dep.valid_to || "現在"
}</small> }</small>
<button class="btn btn-primary" onclick='editDependent(${JSON.stringify( <button class="btn btn-primary" onclick='editDependent(${JSON.stringify(
dep dep,
)})' style="padding: 3px 8px; font-size: 11px; margin-left: 10px;">編集</button> )})' style="padding: 3px 8px; font-size: 11px; margin-left: 10px;">編集</button>
<button class="btn btn-secondary" onclick="deleteDependent(${ <button class="btn btn-secondary" onclick="deleteDependent(${
dep.dependent_id dep.dependent_id
@@ -1001,7 +1127,7 @@
) { ) {
koureiCount = employee.dependents.filter((dep) => { koureiCount = employee.dependents.filter((dep) => {
const taxStatus = getTaxDeductionStatus( const taxStatus = getTaxDeductionStatus(
dep.birth_date dep.birth_date,
); );
return ( return (
taxStatus?.isDeductible && taxStatus?.isDeductible &&
@@ -1048,7 +1174,7 @@
async function editEmployee(employeeId) { async function editEmployee(employeeId) {
try { try {
const response = await fetch( const response = await fetch(
`${API_BASE}/payroll/employees/${employeeId}` `${API_BASE}/payroll/employees/${employeeId}`,
); );
const employee = await response.json(); const employee = await response.json();
currentEmployee = employee; currentEmployee = employee;
@@ -1178,7 +1304,7 @@
const filePath = await uploadFileToServer( const filePath = await uploadFileToServer(
fileInput.files[0], fileInput.files[0],
employeeCode, employeeCode,
"image" "image",
); );
photoUploads[field] = filePath; photoUploads[field] = filePath;
} catch (error) { } catch (error) {
@@ -1214,7 +1340,7 @@
method: "PUT", method: "PUT",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify(data), body: JSON.stringify(data),
} },
); );
if (response.ok) { if (response.ok) {
@@ -1326,7 +1452,7 @@
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify(data), body: JSON.stringify(data),
} },
); );
if (response.ok) { if (response.ok) {
@@ -1408,7 +1534,7 @@
`${API_BASE}/payroll/employees/dependents/${dependentId}`, `${API_BASE}/payroll/employees/dependents/${dependentId}`,
{ {
method: "DELETE", method: "DELETE",
} },
); );
if (response.ok) { if (response.ok) {
@@ -1542,7 +1668,7 @@
method: "PUT", method: "PUT",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify(data), body: JSON.stringify(data),
} },
); );
if (response.ok) { if (response.ok) {