ttvtlb's picture
Tạo một hệ thống quản lý công việc, người dùng có thể thêm task, đánh dấu tác vụ ưu tiên, báo cáo kpi hàng ngày
b1a9ea7 verified
class KpiReport extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
.kpi-container {
@apply bg-white rounded-lg shadow-md p-6;
}
.kpi-header {
@apply flex justify-between items-center mb-4;
}
.kpi-title {
@apply text-xl font-semibold text-gray-800;
}
.kpi-date {
@apply text-sm text-gray-500;
}
.kpi-stats {
@apply grid grid-cols-3 gap-4 mb-6;
}
.stat-card {
@apply p-4 rounded-lg text-center;
}
.stat-value {
@apply text-2xl font-bold mb-1;
}
.stat-label {
@apply text-sm text-gray-500;
}
.completed {
@apply bg-green-50 text-green-600;
}
.pending {
@apply bg-yellow-50 text-yellow-600;
}
.overdue {
@apply bg-red-50 text-red-600;
}
.kpi-table {
@apply w-full border-collapse;
}
.kpi-table th {
@apply text-left py-2 px-4 bg-gray-50 text-gray-600 text-sm font-medium;
}
.kpi-table td {
@apply py-3 px-4 border-b border-gray-200;
}
.priority-high {
@apply text-red-500;
}
.priority-medium {
@apply text-yellow-500;
}
.priority-low {
@apply text-green-500;
}
</style>
<div class="kpi-container">
<div class="kpi-header">
<h3 class="kpi-title">Daily KPI Report</h3>
<span class="kpi-date" id="reportDate"></span>
</div>
<div class="kpi-stats">
<div class="stat-card completed">
<div class="stat-value" id="completedCount">0</div>
<div class="stat-label">Completed</div>
</div>
<div class="stat-card pending">
<div class="stat-value" id="pendingCount">0</div>
<div class="stat-label">Pending</div>
</div>
<div class="stat-card overdue">
<div class="stat-value" id="overdueCount">0</div>
<div class="stat-label">Overdue</div>
</div>
</div>
<table class="kpi-table">
<thead>
<tr>
<th>Task</th>
<th>Priority</th>
<th>Status</th>
<th>Due Date</th>
</tr>
</thead>
<tbody id="kpiTasks">
<!-- Tasks will be populated here -->
</tbody>
</table>
</div>
`;
// Set current date
const today = new Date();
this.shadowRoot.getElementById('reportDate').textContent = today.toLocaleDateString();
}
updateStats(tasks) {
const completed = tasks.filter(t => t.completed).length;
const pending = tasks.filter(t => !t.completed && new Date(t.dueDate) >= new Date()).length;
const overdue = tasks.filter(t => !t.completed && new Date(t.dueDate) < new Date()).length;
this.shadowRoot.getElementById('completedCount').textContent = completed;
this.shadowRoot.getElementById('pendingCount').textContent = pending;
this.shadowRoot.getElementById('overdueCount').textContent = overdue;
const tbody = this.shadowRoot.getElementById('kpiTasks');
tbody.innerHTML = '';
tasks.forEach(task => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${task.title}</td>
<td class="priority-${task.priority}">${task.priority.charAt(0).toUpperCase() + task.priority.slice(1)}</td>
<td>${task.completed ? 'Completed' : new Date(task.dueDate) < new Date() ? 'Overdue' : 'Pending'}</td>
<td>${new Date(task.dueDate).toLocaleDateString()}</td>
`;
tbody.appendChild(row);
});
}
}
customElements.define('kpi-report', KpiReport);