added some function for choose date

This commit is contained in:
2026-06-06 09:05:26 +02:00
parent 75e51dacdc
commit fa6f041f12
4 changed files with 215 additions and 5 deletions
+56
View File
@@ -242,6 +242,62 @@ body::before {
color: var(--text-muted);
}
.text-sm {
font-size: 0.875rem;
}
.text-muted {
color: var(--text-muted);
}
.mb-2 {
margin-bottom: 0.5rem;
}
.table-responsive {
overflow-x: auto;
}
.preview-table {
width: 100%;
border-collapse: collapse;
margin-top: 0.5rem;
background: rgba(255, 255, 255, 0.02);
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
}
.preview-table th, .preview-table td {
padding: 0.75rem 1rem;
text-align: left;
border-bottom: 1px solid var(--border);
}
.preview-table th {
background: rgba(255, 255, 255, 0.04);
font-weight: 600;
color: var(--text-primary);
}
.preview-table tr {
transition: var(--transition);
cursor: pointer;
}
.preview-table tbody tr:hover {
background: rgba(255, 255, 255, 0.06);
}
.preview-table tbody tr.excluded {
opacity: 0.5;
background: rgba(239, 68, 68, 0.05);
}
.preview-table tbody tr.excluded td {
text-decoration: line-through;
}
/* File Preview */
.file-preview {
margin-top: 1.5rem;
+27 -2
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ZynkTime - Time Reporting</title>
<link rel="stylesheet" href="/static/css/style.css?v=2">
<link rel="stylesheet" href="/static/css/style.css?v=3">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body>
@@ -66,6 +66,31 @@
</div>
</div>
<!-- Time Entries Preview Table -->
<div id="previewContainer" style="display: none; margin-top: 1.5rem;">
<h4>Preview Time Entries</h4>
<p class="text-sm text-muted mb-2">Click on a row or checkbox to exclude it from reporting.</p>
<div class="table-responsive">
<table class="preview-table">
<thead>
<tr>
<th width="50"><input type="checkbox" id="selectAllPreview" checked></th>
<th>Date</th>
<th>Hours</th>
</tr>
</thead>
<tbody id="previewTableBody">
</tbody>
<tfoot>
<tr>
<td colspan="2" align="right"><strong>Total Selected:</strong></td>
<td><strong id="previewTotalHours">0.00</strong></td>
</tr>
</tfoot>
</table>
</div>
</div>
<!-- Project Selection -->
<div class="form-group">
<label for="projectSelect">Select Project</label>
@@ -155,6 +180,6 @@
<p>Processing your request...</p>
</div>
<script src="/static/js/app.js?v=2"></script>
<script src="/static/js/app.js?v=3"></script>
</body>
</html>
+96 -2
View File
@@ -2,6 +2,7 @@
let selectedFile = null;
let projects = {};
let lastEvents = [];
let previewData = [];
// DOM Elements
const uploadZone = document.getElementById('uploadZone');
@@ -203,18 +204,105 @@ function setFile(file) {
fileSize.textContent = formatFileSize(file.size);
updateSubmitButton();
generatePreview(file);
}
async function generatePreview(file) {
const formData = new FormData();
formData.append('file', file);
showLoading(true);
try {
const response = await fetch('/preview-time', {
method: 'POST',
body: formData
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.detail || `HTTP ${response.status}`);
}
renderPreview(data);
} catch (error) {
console.error('Preview error:', error);
showToast('Failed to generate preview: ' + error.message, 'error');
document.getElementById('previewContainer').style.display = 'none';
} finally {
showLoading(false);
}
}
function renderPreview(data) {
previewData = data.map(item => ({...item, selected: true}));
const container = document.getElementById('previewContainer');
container.style.display = 'block';
updatePreviewTable();
document.getElementById('selectAllPreview').onchange = (e) => {
const checked = e.target.checked;
previewData.forEach(item => item.selected = checked);
updatePreviewTable();
};
}
// Ensure togglePreviewItem is available globally for inline onclick handlers
window.togglePreviewItem = function(index) {
previewData[index].selected = !previewData[index].selected;
// Update "select all" checkbox state
const allSelected = previewData.every(item => item.selected);
const noneSelected = previewData.every(item => !item.selected);
const selectAllCheckbox = document.getElementById('selectAllPreview');
selectAllCheckbox.checked = allSelected;
selectAllCheckbox.indeterminate = !allSelected && !noneSelected;
updatePreviewTable();
};
function updatePreviewTable() {
const tbody = document.getElementById('previewTableBody');
tbody.innerHTML = '';
let totalHours = 0;
let selectedCount = 0;
previewData.forEach((item, index) => {
if (item.selected) {
totalHours += item.hours;
selectedCount++;
}
const tr = document.createElement('tr');
if (!item.selected) tr.classList.add('excluded');
tr.innerHTML = `
<td><input type="checkbox" ${item.selected ? 'checked' : ''} onclick="event.stopPropagation(); togglePreviewItem(${index})"></td>
<td>${item.date}</td>
<td>${item.hours.toFixed(2)}</td>
`;
tr.onclick = () => togglePreviewItem(index);
tbody.appendChild(tr);
});
document.getElementById('previewTotalHours').textContent = totalHours.toFixed(2);
updateSubmitButton();
}
function removeFile() {
selectedFile = null;
previewData = [];
fileInput.value = '';
uploadZone.style.display = 'block';
filePreview.style.display = 'none';
document.getElementById('previewContainer').style.display = 'none';
updateSubmitButton();
}
function updateSubmitButton() {
submitBtn.disabled = !(selectedFile && projectSelect.value);
const hasSelectedDates = previewData.length === 0 || previewData.some(item => item.selected);
submitBtn.disabled = !(selectedFile && projectSelect.value && hasSelectedDates);
}
function setDefaultDateRange() {
@@ -238,10 +326,16 @@ async function handleSubmit() {
const projectName = projectSelect.value;
const excludedDates = previewData
.filter(item => !item.selected)
.map(item => item.date)
.join(',');
showLoading(true);
try {
const response = await fetch(`/report-time?project_name=${encodeURIComponent(projectName)}`, {
const url = `/report-time?project_name=${encodeURIComponent(projectName)}&excluded_dates=${encodeURIComponent(excludedDates)}`;
const response = await fetch(url, {
method: 'POST',
body: formData
});