390 lines
22 KiB
HTML
390 lines
22 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>PANJIT分機表查詢</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
</head>
|
|
<body class="bg-gray-900 text-gray-100 font-sans text-xs">
|
|
<div class="container mx-auto px-2 py-4">
|
|
<div class="text-center mb-6">
|
|
<h1 class="text-4xl font-extrabold text-white tracking-tight">PANJIT分機表查詢</h1>
|
|
<p class="mt-2 text-gray-400 text-base">快速查詢與管理分機資料</p>
|
|
</div>
|
|
|
|
<!-- Search and Add Button Section -->
|
|
<div class="flex flex-col md:flex-row justify-between items-center gap-4 mb-6">
|
|
<!-- Search Form -->
|
|
<div class="w-full md:w-1/2 bg-gray-800 p-4 rounded-lg shadow-md border border-gray-700">
|
|
<form action="/" method="get" class="flex items-center">
|
|
<input type="text" name="search" placeholder="搜尋廠別、部門、姓名、分機..." value="{{ search_query }}" class="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-l-md text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-xs">
|
|
<button type="submit" class="bg-blue-600 text-white px-5 py-2 rounded-r-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 transition duration-200 ease-in-out text-sm">搜尋</button>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Buttons Section -->
|
|
<div class="flex flex-col md:flex-row gap-4 w-full md:w-auto">
|
|
<!-- Add New Extension Button -->
|
|
<button id="open-add-modal" class="w-full whitespace-nowrap bg-green-600 text-white px-6 py-2 rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 shadow-lg transition duration-200 ease-in-out text-sm">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 inline-block mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
|
</svg>
|
|
新增分機
|
|
</button>
|
|
|
|
<!-- Batch Upload Button -->
|
|
<button id="open-batch-upload-modal" class="w-full whitespace-nowrap bg-purple-600 text-white px-6 py-2 rounded-md hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-500 shadow-lg transition duration-200 ease-in-out text-sm">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 inline-block mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
|
</svg>
|
|
批次增修分機
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Batch Upload Modal -->
|
|
<div id="batch-upload-modal" class="fixed inset-0 bg-gray-900 bg-opacity-75 flex items-center justify-center z-50 hidden">
|
|
<div class="bg-gray-800 p-6 rounded-lg shadow-2xl w-full max-w-3xl border border-gray-700 transform transition-all duration-300 scale-95 opacity-0" id="batch-upload-modal-content">
|
|
<div class="flex justify-between items-center pb-3 border-b border-gray-700 mb-4">
|
|
<h3 class="text-2xl font-bold text-white">批次增修分機</h3>
|
|
<button id="close-batch-upload-modal" class="text-gray-400 hover:text-gray-200 text-3xl leading-none">×</button>
|
|
</div>
|
|
<div class="mt-3">
|
|
<div class="mb-4">
|
|
<label for="batch-csv-file" class="block text-gray-300 text-sm font-bold mb-2">選擇 CSV 檔案:</label>
|
|
<input type="file" id="batch-csv-file" accept=".csv" class="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 text-xs">
|
|
<p class="mt-2 text-gray-400 text-xs">請下載 <a href="/download_template" class="text-blue-400 hover:underline" download>CSV 範本</a> 以確保檔案格式正確。</p>
|
|
</div>
|
|
<div id="csv-preview" class="bg-gray-700 p-3 rounded-md overflow-auto max-h-60 mb-4 text-gray-300 text-xs">
|
|
<p class="text-center text-gray-400">請選擇一個 CSV 檔案以預覽內容</p>
|
|
</div>
|
|
<div class="flex justify-end space-x-3">
|
|
<button id="confirm-batch-upload" class="bg-green-600 text-white px-5 py-2 rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 transition duration-200 ease-in-out text-sm" disabled>確認上傳</button>
|
|
<button id="cancel-batch-upload" class="bg-gray-600 text-white px-5 py-2 rounded-md hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-500 transition duration-200 ease-in-out text-sm">取消</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Add Extension Modal -->
|
|
<div id="add-modal" class="fixed inset-0 bg-gray-900 bg-opacity-75 flex items-center justify-center z-50 hidden">
|
|
<div class="bg-gray-800 p-6 rounded-lg shadow-2xl w-full max-w-sm border border-gray-700 transform transition-all duration-300 scale-95 opacity-0" id="add-modal-content">
|
|
<div class="flex justify-between items-center pb-3 border-b border-gray-700 mb-4">
|
|
<h3 class="text-2xl font-bold text-white">新增分機</h3>
|
|
<button id="close-add-modal" class="text-gray-400 hover:text-gray-200 text-3xl leading-none">×</button>
|
|
</div>
|
|
<div class="mt-3">
|
|
<form id="add-form" class="grid grid-cols-1 gap-3">
|
|
<input type="text" name="plant" placeholder="廠別" required class="px-3 py-1 bg-gray-700 border border-gray-600 rounded-md text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 text-xs">
|
|
<input type="text" name="department" placeholder="部門" required class="px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 text-xs">
|
|
<input type="text" name="name" placeholder="姓名" required class="px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 text-xs">
|
|
<input type="text" name="position" placeholder="職位" class="px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 text-xs">
|
|
<input type="text" name="extension" placeholder="分機號碼" required class="px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 text-xs">
|
|
<button type="submit" class="bg-green-600 text-white px-5 py-2 rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 transition duration-200 ease-in-out text-sm mt-3">確認新增</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Data Table -->
|
|
<div class="bg-gray-800 shadow-xl rounded-lg overflow-hidden border border-gray-700">
|
|
<table class="min-w-full leading-normal">
|
|
<thead class="bg-gray-700 text-gray-300 uppercase text-xs">
|
|
<tr>
|
|
<th class="px-4 py-2 border-b-2 border-gray-600 text-left tracking-wider">廠別</th>
|
|
<th class="px-4 py-2 border-b-2 border-gray-600 text-left tracking-wider">部門</th>
|
|
<th class="px-4 py-2 border-b-2 border-gray-600 text-left tracking-wider">姓名</th>
|
|
<th class="px-4 py-2 border-b-2 border-gray-600 text-left tracking-wider">職位</th>
|
|
<th class="px-4 py-2 border-b-2 border-gray-600 text-left tracking-wider">分機</th>
|
|
<th class="px-4 py-2 border-b-2 border-gray-600 text-left tracking-wider">操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="text-gray-200 text-xs">
|
|
{% for row in data %}
|
|
<tr class="border-b border-gray-700 hover:bg-gray-700 transition duration-150 ease-in-out">
|
|
<td class="px-4 py-2">{{ row.plant }}</td>
|
|
<td class="px-4 py-2">{{ row.department }}</td>
|
|
<td class="px-4 py-2">{{ row.name }}</td>
|
|
<td class="px-4 py-2">{{ row.position }}</td>
|
|
<td class="px-4 py-2">{{ row.extension }}</td>
|
|
<td class="px-4 py-2 flex space-x-1">
|
|
<button class="bg-yellow-600 text-white px-2 py-1 rounded-md hover:bg-yellow-700 focus:outline-none focus:ring-2 focus:ring-yellow-500 transition duration-200 ease-in-out text-xs" onclick="editRow(this, {{ row.id }})">編輯</button>
|
|
<button class="bg-red-600 text-white px-2 py-1 rounded-md hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 transition duration-200 ease-in-out text-xs" onclick="deleteRow({{ row.id }})">刪除</button>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Modal functionality
|
|
const addModal = document.getElementById('add-modal');
|
|
const addModalContent = document.getElementById('add-modal-content');
|
|
const openAddModalBtn = document.getElementById('open-add-modal');
|
|
const closeAddModalBtn = document.getElementById('close-add-modal');
|
|
|
|
function showModal() {
|
|
addModal.classList.remove('hidden');
|
|
setTimeout(() => {
|
|
addModalContent.classList.remove('opacity-0', 'scale-95');
|
|
addModalContent.classList.add('opacity-100', 'scale-100');
|
|
}, 50);
|
|
}
|
|
|
|
function hideModal() {
|
|
addModalContent.classList.remove('opacity-100', 'scale-100');
|
|
addModalContent.classList.add('opacity-0', 'scale-95');
|
|
setTimeout(() => {
|
|
addModal.classList.add('hidden');
|
|
}, 300); // Match this to the transition duration
|
|
}
|
|
|
|
openAddModalBtn.addEventListener('click', showModal);
|
|
closeAddModalBtn.addEventListener('click', hideModal);
|
|
|
|
window.addEventListener('click', (event) => {
|
|
if (event.target === addModal) {
|
|
hideModal();
|
|
}
|
|
});
|
|
|
|
document.getElementById('add-form').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(this);
|
|
const data = Object.fromEntries(formData.entries());
|
|
|
|
fetch('/add', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(data)
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.status === 'success') {
|
|
location.reload();
|
|
}
|
|
});
|
|
});
|
|
|
|
function editRow(button, id) {
|
|
const row = button.closest('tr');
|
|
const cells = row.querySelectorAll('td');
|
|
|
|
if (button.textContent === '編輯') {
|
|
cells.forEach((cell, index) => {
|
|
if (index < cells.length - 1) {
|
|
const value = cell.textContent;
|
|
cell.innerHTML = `<input type="text" class="w-full px-2 py-1 bg-gray-700 border border-gray-600 rounded-md text-white text-xs" value="${value}">`;
|
|
}
|
|
});
|
|
button.textContent = '儲存';
|
|
button.classList.remove('bg-yellow-600', 'hover:bg-yellow-700');
|
|
button.classList.add('bg-green-600', 'hover:bg-green-700');
|
|
} else {
|
|
const data = {};
|
|
const inputs = row.querySelectorAll('input');
|
|
data.plant = inputs[0].value;
|
|
data.department = inputs[1].value;
|
|
data.name = inputs[2].value;
|
|
data.position = inputs[3].value;
|
|
data.extension = inputs[4].value;
|
|
|
|
fetch(`/update/${id}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(data)
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.status === 'success') {
|
|
location.reload();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function deleteRow(id) {
|
|
if (confirm('確定要刪除這筆資料嗎?')) {
|
|
fetch(`/delete/${id}`, {
|
|
method: 'POST'
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.status === 'success') {
|
|
location.reload();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Batch Upload Modal functionality
|
|
const batchUploadModal = document.getElementById('batch-upload-modal');
|
|
const batchUploadModalContent = document.getElementById('batch-upload-modal-content');
|
|
const openBatchUploadModalBtn = document.getElementById('open-batch-upload-modal');
|
|
const closeBatchUploadModalBtn = document.getElementById('close-batch-upload-modal');
|
|
const cancelBatchUploadBtn = document.getElementById('cancel-batch-upload');
|
|
const batchCsvFile = document.getElementById('batch-csv-file');
|
|
const csvPreview = document.getElementById('csv-preview');
|
|
const confirmBatchUploadBtn = document.getElementById('confirm-batch-upload');
|
|
|
|
function showBatchUploadModal() {
|
|
batchUploadModal.classList.remove('hidden');
|
|
setTimeout(() => {
|
|
batchUploadModalContent.classList.remove('opacity-0', 'scale-95');
|
|
batchUploadModalContent.classList.add('opacity-100', 'scale-100');
|
|
}, 50);
|
|
}
|
|
|
|
function hideBatchUploadModal() {
|
|
batchUploadModalContent.classList.remove('opacity-100', 'scale-100');
|
|
batchUploadModalContent.classList.add('opacity-0', 'scale-95');
|
|
setTimeout(() => {
|
|
batchUploadModal.classList.add('hidden');
|
|
// Reset file input and preview
|
|
batchCsvFile.value = '';
|
|
csvPreview.innerHTML = '<p class="text-center text-gray-400">請選擇一個 CSV 檔案以預覽內容</p>';
|
|
confirmBatchUploadBtn.disabled = true;
|
|
}, 300);
|
|
}
|
|
|
|
openBatchUploadModalBtn.addEventListener('click', showBatchUploadModal);
|
|
closeBatchUploadModalBtn.addEventListener('click', hideBatchUploadModal);
|
|
cancelBatchUploadBtn.addEventListener('click', hideBatchUploadModal);
|
|
|
|
window.addEventListener('click', (event) => {
|
|
if (event.target === batchUploadModal) {
|
|
hideBatchUploadModal();
|
|
}
|
|
});
|
|
|
|
batchCsvFile.addEventListener('change', function(event) {
|
|
const file = event.target.files[0];
|
|
if (file) {
|
|
const reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
const text = e.target.result;
|
|
const lines = text.split(/\r\n|\n/);
|
|
const headers = ["plant", "department", "csv_id", "position", "extension"]; // Internal keys
|
|
const chineseHeaders = ["廠別", "部門", "姓名", "職位", "分機"];
|
|
|
|
if (lines.length < 2) {
|
|
csvPreview.innerHTML = '<p class="text-center text-gray-400">CSV 檔案內容為空或格式不正確。</p>';
|
|
confirmBatchUploadBtn.disabled = true;
|
|
return;
|
|
}
|
|
|
|
// Parse CSV data for sending to backend
|
|
const csvData = [];
|
|
for (let i = 1; i < lines.length; i++) {
|
|
const row = lines[i].split(',');
|
|
if (row.length >= 5) { // Ensure row has enough columns
|
|
csvData.push({
|
|
plant: row[0],
|
|
department: row[1],
|
|
csv_id: row[2],
|
|
position: row[3],
|
|
extension: row[4]
|
|
});
|
|
}
|
|
}
|
|
|
|
if (csvData.length === 0) {
|
|
csvPreview.innerHTML = '<p class="text-center text-gray-400">CSV 檔案中沒有有效的資料行。</p>';
|
|
confirmBatchUploadBtn.disabled = true;
|
|
return;
|
|
}
|
|
|
|
// Send data to backend for validation
|
|
fetch('/validate_csv_data', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(csvData)
|
|
})
|
|
.then(response => response.json())
|
|
.then(validatedRows => {
|
|
let html = '<table class="min-w-full bg-gray-700 text-gray-300 text-xs"><thead><tr>';
|
|
chineseHeaders.forEach(h => {
|
|
html += `<th class="px-2 py-1 border-b border-gray-600 text-left">${h}</th>`;
|
|
});
|
|
html += '<th class="px-2 py-1 border-b border-gray-600 text-left">狀態</th>';
|
|
html += '</tr></thead><tbody>';
|
|
|
|
validatedRows.forEach(rowData => {
|
|
const original = rowData.original_data;
|
|
const diff = rowData.diff;
|
|
const status = rowData.status;
|
|
|
|
html += '<tr>';
|
|
html += `<td class="px-2 py-1 border-b border-gray-600">${original.plant}</td>`;
|
|
html += `<td class="px-2 py-1 border-b border-gray-600 ${diff.department ? 'text-red-400' : ''}">${original.department}</td>`;
|
|
html += `<td class="px-2 py-1 border-b border-gray-600">${original.csv_id}</td>`;
|
|
html += `<td class="px-2 py-1 border-b border-gray-600 ${diff.position ? 'text-red-400' : ''}">${original.position}</td>`;
|
|
html += `<td class="px-2 py-1 border-b border-gray-600 ${diff.extension ? 'text-red-400' : ''}">${original.extension}</td>`;
|
|
|
|
let statusText = '';
|
|
if (status === 'new') {
|
|
statusText = '<span class="text-green-400">新增</span>';
|
|
} else if (status === 'update') {
|
|
statusText = '<span class="text-yellow-400">更新</span>';
|
|
} else {
|
|
statusText = '<span class="text-gray-400">無變更</span>';
|
|
}
|
|
html += `<td class="px-2 py-1 border-b border-gray-600">${statusText}</td>`;
|
|
html += '</tr>';
|
|
});
|
|
|
|
html += '</tbody></table>';
|
|
csvPreview.innerHTML = html;
|
|
confirmBatchUploadBtn.disabled = false;
|
|
})
|
|
.catch(error => {
|
|
console.error('Error validating CSV data:', error);
|
|
csvPreview.innerHTML = '<p class="text-center text-red-400">驗證 CSV 檔案時發生錯誤。</p>';
|
|
confirmBatchUploadBtn.disabled = true;
|
|
});
|
|
};
|
|
reader.readAsText(file, 'Big5'); // Assuming Big5 encoding for CSV
|
|
} else {
|
|
csvPreview.innerHTML = '<p class="text-center text-gray-400">請選擇一個 CSV 檔案以預覽內容</p>';
|
|
confirmBatchUploadBtn.disabled = true;
|
|
}
|
|
});
|
|
|
|
confirmBatchUploadBtn.addEventListener('click', function() {
|
|
const file = batchCsvFile.files[0];
|
|
if (file) {
|
|
const formData = new FormData();
|
|
formData.append('csv_file', file);
|
|
|
|
fetch('/batch_upload', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.status === 'success') {
|
|
alert('批次上傳成功!');
|
|
hideBatchUploadModal();
|
|
location.reload();
|
|
} else {
|
|
alert('批次上傳失敗: ' + data.message);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
alert('批次上傳過程中發生錯誤。');
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |