| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Weekly Project Report | SlateSync</title> |
| <link rel="icon" type="image/x-icon" href="/static/favicon.ico"> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://unpkg.com/feather-icons"></script> |
| <style> |
| .progress-ring__circle { |
| transition: stroke-dashoffset 0.5s; |
| transform: rotate(-90deg); |
| transform-origin: 50% 50%; |
| } |
| .card-hover:hover { |
| transform: translateY(-5px); |
| box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); |
| } |
| .badge { |
| @apply px-2 py-1 text-xs rounded-full; |
| } |
| .status-completed { |
| @apply bg-green-100 text-green-800; |
| } |
| .status-inprogress { |
| @apply bg-blue-100 text-blue-800; |
| } |
| .level-critical { |
| @apply bg-red-100 text-red-800; |
| } |
| .level-high { |
| @apply bg-orange-100 text-orange-800; |
| } |
| .level-medium { |
| @apply bg-yellow-100 text-yellow-800; |
| } |
| .level-low { |
| @apply bg-gray-100 text-gray-800; |
| } |
| </style> |
| </head> |
| <body class="bg-primary-50 min-h-screen"> |
| <div class="container mx-auto px-4 py-8"> |
| |
| <header class="mb-10"> |
| <div class="flex justify-between items-center mb-6"> |
| <div class="flex items-center"> |
| <div class="w-10 h-10 rounded-full bg-secondary-500 flex items-center justify-center mr-3"> |
| <i data-feather="file-text" class="text-white"></i> |
| </div> |
| <h1 class="text-3xl font-bold text-primary-800">Weekly Project Report</h1> |
| </div> |
| <div class="flex space-x-4"> |
| <button class="bg-white hover:bg-primary-50 border border-primary-200 px-4 py-2 rounded-lg flex items-center transition"> |
| <i data-feather="download" class="mr-2"></i> |
| Export PDF |
| </button> |
| <button class="bg-secondary-500 hover:bg-secondary-600 text-white px-4 py-2 rounded-lg flex items-center transition"> |
| <i data-feather="share-2" class="mr-2"></i> |
| Share Report |
| </button> |
| </div> |
| </div> |
| |
| <div class="bg-white rounded-xl shadow-md p-6"> |
| <div class="grid grid-cols-1 md:grid-cols-4 gap-6"> |
| <div> |
| <h3 class="text-sm font-medium text-primary-400 mb-1">Report Period</h3> |
| <p class="text-primary-800 font-medium" id="report-period">Jun 5 - Jun 11, 2023</p> |
| </div> |
| <div> |
| <h3 class="text-sm font-medium text-primary-400 mb-1">Project</h3> |
| <p class="text-primary-800 font-medium" id="project-name">Website Redesign</p> |
| </div> |
| <div> |
| <h3 class="text-sm font-medium text-primary-400 mb-1">Team Members</h3> |
| <p class="text-primary-800 font-medium" id="team-members">8 (6 active)</p> |
| </div> |
| <div> |
| <h3 class="text-sm font-medium text-primary-400 mb-1">Generated On</h3> |
| <p class="text-primary-800 font-medium" id="generate-time">Jun 8, 2023 10:00 AM</p> |
| </div> |
| </div> |
| </div> |
| </header> |
|
|
| |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-8"> |
| |
| <div class="lg:col-span-2"> |
| |
| <div class="bg-white rounded-xl shadow-md p-6 mb-8"> |
| <h2 class="text-xl font-bold text-primary-800 mb-6">Weekly Summary</h2> |
| <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> |
| <div class="bg-primary-50 rounded-lg p-4"> |
| <h3 class="text-sm font-medium text-primary-500 mb-1">Tasks Completed</h3> |
| <div class="flex items-end justify-between"> |
| <span class="text-2xl font-bold text-primary-800">8</span> |
| <span class="text-sm text-green-500">+2 from last week</span> |
| </div> |
| </div> |
| <div class="bg-primary-50 rounded-lg p-4"> |
| <h3 class="text-sm font-medium text-primary-500 mb-1">Value Points</h3> |
| <div class="flex items-end justify-between"> |
| <span class="text-2xl font-bold text-primary-800">120</span> |
| <span class="text-sm text-green-500">80% achieved</span> |
| </div> |
| </div> |
| <div class="bg-primary-50 rounded-lg p-4"> |
| <h3 class="text-sm font-medium text-primary-500 mb-1">Defects Fixed</h3> |
| <div class="flex items-end justify-between"> |
| <span class="text-2xl font-bold text-primary-800">2</span> |
| <span class="text-sm text-yellow-500">4 remaining</span> |
| </div> |
| </div> |
| <div class="bg-primary-50 rounded-lg p-4"> |
| <h3 class="text-sm font-medium text-primary-500 mb-1">Avg Fix Time</h3> |
| <div class="flex items-end justify-between"> |
| <span class="text-2xl font-bold text-primary-800">1.5d</span> |
| <span class="text-sm text-green-500">Improved 0.5d</span> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-white rounded-xl shadow-md p-6 mb-8"> |
| <div class="flex justify-between items-center mb-6"> |
| <h2 class="text-xl font-bold text-primary-800">Completed Tasks</h2> |
| <span class="text-sm text-primary-500">8 tasks completed (64h planned, 72.5h actual)</span> |
| </div> |
| <div class="overflow-x-auto"> |
| <table class="w-full"> |
| <thead class="text-left text-primary-500 border-b border-primary-100"> |
| <tr> |
| <th class="pb-3 font-medium">Task</th> |
| <th class="pb-3 font-medium">Assignee</th> |
| <th class="pb-3 font-medium">Hours</th> |
| <th class="pb-3 font-medium">Value</th> |
| <th class="pb-3 font-medium">Status</th> |
| </tr> |
| </thead> |
| <tbody class="divide-y divide-primary-100" id="completed-tasks"> |
| |
| </tbody> |
| </table> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-white rounded-xl shadow-md p-6 mb-8"> |
| <div class="flex justify-between items-center mb-6"> |
| <h2 class="text-xl font-bold text-primary-800">Defects Tracking</h2> |
| <div class="flex space-x-2"> |
| <span class="text-sm text-green-500">2 fixed</span> |
| <span class="text-sm text-primary-400">|</span> |
| <span class="text-sm text-red-500">3 new</span> |
| <span class="text-sm text-primary-400">|</span> |
| <span class="text-sm text-yellow-500">4 open</span> |
| </div> |
| </div> |
| <div class="overflow-x-auto"> |
| <table class="w-full"> |
| <thead class="text-left text-primary-500 border-b border-primary-100"> |
| <tr> |
| <th class="pb-3 font-medium">Defect</th> |
| <th class="pb-3 font-medium">Assignee</th> |
| <th class="pb-3 font-medium">Level</th> |
| <th class="pb-3 font-medium">Status</th> |
| <th class="pb-3 font-medium">Fix Time</th> |
| </tr> |
| </thead> |
| <tbody class="divide-y divide-primary-100" id="defects-list"> |
| |
| </tbody> |
| </table> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-white rounded-xl shadow-md p-6"> |
| <h2 class="text-xl font-bold text-primary-800 mb-6">Key Events</h2> |
| <div class="space-y-4" id="events-list"> |
| |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div> |
| |
| <div class="bg-white rounded-xl shadow-md p-6 mb-8"> |
| <h2 class="text-xl font-bold text-primary-800 mb-6">Progress Overview</h2> |
| <div class="flex flex-col items-center"> |
| <div class="relative w-40 h-40 mb-4"> |
| <svg class="w-full h-full" viewBox="0 0 100 100"> |
| <circle class="text-primary-100" stroke-width="8" stroke="currentColor" fill="transparent" r="40" cx="50" cy="50" /> |
| <circle class="text-secondary-500" stroke-width="8" stroke-dasharray="251.2" stroke-dashoffset="50.24" stroke-linecap="round" stroke="currentColor" fill="transparent" r="40" cx="50" cy="50" /> |
| </svg> |
| <div class="absolute inset-0 flex items-center justify-center"> |
| <span class="text-3xl font-bold text-primary-800">80%</span> |
| </div> |
| </div> |
| <div class="grid grid-cols-2 gap-4 w-full"> |
| <div class="text-center"> |
| <p class="text-sm text-primary-500">Planned</p> |
| <p class="font-bold text-primary-800">80.5h</p> |
| </div> |
| <div class="text-center"> |
| <p class="text-sm text-primary-500">Actual</p> |
| <p class="font-bold text-primary-800">72.5h</p> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-white rounded-xl shadow-md p-6 mb-8"> |
| <h2 class="text-xl font-bold text-primary-800 mb-6">Team Contributions</h2> |
| <div class="space-y-4" id="team-contributions"> |
| |
| </div> |
| </div> |
|
|
| |
| <div class="bg-white rounded-xl shadow-md p-6"> |
| <h2 class="text-xl font-bold text-primary-800 mb-6">Next Week Plan</h2> |
| <div class="mb-4"> |
| <p class="text-sm text-primary-500 mb-1">Planned Tasks</p> |
| <p class="font-bold text-primary-800">12 tasks (96h, 180 points)</p> |
| </div> |
| <div class="space-y-3" id="next-week-tasks"> |
| |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| feather.replace(); |
| |
| |
| const reportData = { |
| projectId: "PRJ-2023-001", |
| reportPeriod: { |
| startDate: "2024-01-01", |
| endDate: "2024-01-07" |
| }, |
| thisWeek: { |
| statistics: { |
| plannedTasks: { count: 10, totalBaselineHours: 80.5, totalValuePoints: 150 }, |
| completedTasks: { count: 8, totalBaselineHours: 64.0, totalActualHours: 72.5, totalValuePoints: 120 }, |
| inProgressTasks: { count: 5, totalBaselineHours: 40.0, totalValuePoints: 75 }, |
| newDefects: { count: 3 }, |
| fixedDefects: { count: 2, avgFixDays: 1.5 }, |
| unfixedDefects: { count: 4 }, |
| newEvents: { count: 2, totalValuePoints: 30 }, |
| projectMembers: { totalCount: 8, activeCount: 6 } |
| }, |
| taskList: [ |
| { |
| id: "TASK-001", |
| taskNo: "TASK-001", |
| content: "Implement user authentication", |
| assigneeName: "Sarah Johnson", |
| planCompletionDate: "2024-01-05T00:00:00", |
| actualCompletionDate: "2024-01-04T00:00:00", |
| statusName: "Completed", |
| baselineHours: 8.0, |
| actualHours: 9.5, |
| complexityName: "Medium", |
| valuePoints: 15 |
| }, |
| { |
| id: "TASK-002", |
| taskNo: "TASK-002", |
| content: "Design homepage mockups", |
| assigneeName: "Michael Chen", |
| planCompletionDate: "2024-01-03T00:00:00", |
| actualCompletionDate: "2024-01-03T00:00:00", |
| statusName: "Completed", |
| baselineHours: 12.0, |
| actualHours: 10.0, |
| complexityName: "High", |
| valuePoints: 25 |
| } |
| ], |
| defectList: [ |
| { |
| id: "BUG-001", |
| defectNo: "BUG-001", |
| title: "Login fails on mobile", |
| description: "Users unable to login on mobile devices", |
| reporterName: "Alex Rodriguez", |
| assigneeName: "Sarah Johnson", |
| levelName: "Critical", |
| statusName: "Fixed", |
| reportTime: "2024-01-02T10:00:00", |
| fixTime: "2024-01-03T15:30:00", |
| fixDays: 1 |
| }, |
| { |
| id: "BUG-002", |
| defectNo: "BUG-002", |
| title: "Dashboard loading slow", |
| description: "Dashboard takes >5s to load", |
| reporterName: "Emma Wilson", |
| assigneeName: "David Kim", |
| levelName: "High", |
| statusName: "Open", |
| reportTime: "2024-01-04T14:00:00", |
| fixTime: null, |
| fixDays: null |
| } |
| ], |
| eventList: [ |
| { |
| id: "EVT-001", |
| eventTypeName: "Team Meeting", |
| eventDate: "2024-01-03T00:00:00", |
| content: "Weekly sprint planning", |
| memberName: "All", |
| valuePoints: 20, |
| hoursSpent: 2.0 |
| } |
| ] |
| }, |
| nextWeek: { |
| statistics: { |
| plannedTasks: { count: 12, totalBaselineHours: 96.0, totalValuePoints: 180 }, |
| inProgressTasks: { count: 3, totalBaselineHours: 24.0, totalValuePoints: 45 } |
| }, |
| taskList: [ |
| { |
| id: "TASK-101", |
| taskNo: "TASK-101", |
| content: "Implement payment gateway", |
| assigneeName: "Sarah Johnson", |
| planCompletionDate: "2024-01-12T00:00:00", |
| statusName: "Planned", |
| baselineHours: 16.0, |
| complexityName: "High", |
| valuePoints: 30 |
| } |
| ] |
| }, |
| generateTime: "2024-01-08T10:00:00" |
| }; |
| |
| |
| function formatDate(dateStr) { |
| if (!dateStr) return ''; |
| const date = new Date(dateStr); |
| return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); |
| } |
| |
| function formatDateTime(dateStr) { |
| if (!dateStr) return ''; |
| const date = new Date(dateStr); |
| return date.toLocaleString('en-US', { |
| month: 'short', |
| day: 'numeric', |
| hour: '2-digit', |
| minute: '2-digit' |
| }); |
| } |
| |
| |
| document.addEventListener('DOMContentLoaded', function() { |
| |
| document.getElementById('report-period').textContent = |
| `${formatDate(reportData.reportPeriod.startDate)} - ${formatDate(reportData.reportPeriod.endDate)}`; |
| document.getElementById('team-members').textContent = |
| `${reportData.thisWeek.statistics.projectMembers.totalCount} (${reportData.thisWeek.statistics.projectMembers.activeCount} active)`; |
| document.getElementById('generate-time').textContent = |
| formatDateTime(reportData.generateTime); |
| |
| |
| const completedTasksContainer = document.getElementById('completed-tasks'); |
| reportData.thisWeek.taskList.forEach(task => { |
| const row = document.createElement('tr'); |
| row.className = 'hover:bg-primary-50'; |
| row.innerHTML = ` |
| <td class="py-3"> |
| <div class="font-medium text-primary-700">${task.taskNo}</div> |
| <div class="text-sm text-primary-500">${task.content}</div> |
| </td> |
| <td class="py-3">${task.assigneeName}</td> |
| <td class="py-3"> |
| <div class="text-sm text-primary-500">${task.baselineHours}h planned</div> |
| <div class="font-medium">${task.actualHours}h actual</div> |
| </td> |
| <td class="py-3">${task.valuePoints}</td> |
| <td class="py-3"> |
| <span class="badge status-completed">${task.statusName}</span> |
| </td> |
| `; |
| completedTasksContainer.appendChild(row); |
| }); |
| |
| |
| const defectsContainer = document.getElementById('defects-list'); |
| reportData.thisWeek.defectList.forEach(defect => { |
| const row = document.createElement('tr'); |
| row.className = 'hover:bg-primary-50'; |
| row.innerHTML = ` |
| <td class="py-3"> |
| <div class="font-medium text-primary-700">${defect.defectNo}</div> |
| <div class="text-sm text-primary-500">${defect.title}</div> |
| </td> |
| <td class="py-3">${defect.assigneeName || 'Unassigned'}</td> |
| <td class="py-3"> |
| <span class="badge level-${defect.levelName.toLowerCase()}">${defect.levelName}</span> |
| </td> |
| <td class="py-3"> |
| <span class="badge ${defect.statusName === 'Fixed' ? 'status-completed' : 'status-inprogress'}"> |
| ${defect.statusName} |
| </span> |
| </td> |
| <td class="py-3"> |
| ${defect.fixTime ? `${defect.fixDays}d` : '-'} |
| </td> |
| `; |
| defectsContainer.appendChild(row); |
| }); |
| |
| |
| const eventsContainer = document.getElementById('events-list'); |
| reportData.thisWeek.eventList.forEach(event => { |
| const eventElement = document.createElement('div'); |
| eventElement.className = 'flex items-start'; |
| eventElement.innerHTML = ` |
| <div class="mr-4 mt-1"> |
| <div class="w-8 h-8 rounded-lg bg-secondary-100 flex items-center justify-center"> |
| <i data-feather="calendar" class="text-secondary-500"></i> |
| </div> |
| </div> |
| <div> |
| <h4 class="font-medium text-primary-800">${event.eventTypeName}</h4> |
| <p class="text-sm text-primary-500 mt-1">${event.content}</p> |
| <div class="flex items-center mt-2 text-sm text-primary-400"> |
| <i data-feather="user" class="w-3 h-3 mr-1"></i> |
| <span class="mr-3">${event.memberName}</span> |
| <i data-feather="clock" class="w-3 h-3 mr-1"></i> |
| <span>${event.hoursSpent}h (${event.valuePoints}pts)</span> |
| </div> |
| </div> |
| `; |
| eventsContainer.appendChild(eventElement); |
| }); |
| |
| |
| const teamContainer = document.getElementById('team-contributions'); |
| const teamMembers = [ |
| { name: "Sarah Johnson", tasks: 8, value: 120 }, |
| { name: "Michael Chen", tasks: 5, value: 85 }, |
| { name: "Alex Rodriguez", tasks: 3, value: 45 } |
| ]; |
| teamMembers.forEach(member => { |
| const memberElement = document.createElement('div'); |
| memberElement.className = 'flex items-center'; |
| memberElement.innerHTML = ` |
| <div class="w-10 h-10 rounded-full bg-secondary-100 flex items-center justify-center mr-3"> |
| <i data-feather="user" class="text-secondary-500"></i> |
| </div> |
| <div class="flex-1"> |
| <h4 class="font-medium text-primary-800">${member.name}</h4> |
| <div class="flex justify-between text-sm text-primary-500"> |
| <span>${member.tasks} tasks</span> |
| <span>${member.value} points</span> |
| </div> |
| </div> |
| `; |
| teamContainer.appendChild(memberElement); |
| }); |
| |
| |
| const nextWeekContainer = document.getElementById('next-week-tasks'); |
| reportData.nextWeek.taskList.forEach(task => { |
| const taskElement = document.createElement('div'); |
| taskElement.className = 'flex items-start'; |
| taskElement.innerHTML = ` |
| <div class="mr-3 mt-1"> |
| <div class="w-5 h-5 rounded-full bg-secondary-100 flex items-center justify-center"> |
| <i data-feather="flag" class="text-secondary-500 w-3 h-3"></i> |
| </div> |
| </div> |
| <div> |
| <p class="text-primary-700">${task.content}</p> |
| <div class="text-xs text-primary-400 mt-1"> |
| ${task.assigneeName} • ${task.baselineHours}h • ${task.valuePoints}pts |
| </div> |
| </div> |
| `; |
| nextWeekContainer.appendChild(taskElement); |
| }); |
| |
| |
| feather.replace(); |
| }); |
| |
| |
| document.addEventListener('DOMContentLoaded', function() { |
| const sections = document.querySelectorAll('.bg-white'); |
| sections.forEach((section, index) => { |
| section.style.opacity = '0'; |
| section.style.transform = 'translateY(20px)'; |
| section.style.transition = 'all 0.5s ease ' + (index * 0.1) + 's'; |
| |
| setTimeout(() => { |
| section.style.opacity = '1'; |
| section.style.transform = 'translateY(0)'; |
| }, 100); |
| }); |
| }); |
| </script> |
| </body> |
| </html> |