Spaces:
Running
Running
| 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); |