fabfdb

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weekly Status Report Generator</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min...
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 15px;
background: #fafbfc;
}

.report-container {
width: 1200px;
min-height: 700px;
background: white;
padding: 25px;
margin: 0 auto;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
border-radius: 6px;
}

/* Header */
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}

.title {
font-size: 22px;
font-weight: 700;
color: #000000;
}

.date {
font-size: 14px;
color: #444444;
font-weight: 500;
}

/* Main Layout */
.main-layout {
display: flex;
gap: 20px;
}

/* Task Columns */
.tasks-container {
flex: 1;
display: flex;
gap: 20px;
}

.task-column {
flex: 1;
display: flex;
flex-direction: column;
}

.column-header {
background: #f3f4f6;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 15px;
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid #e5e7eb;
}

.column-title {
font-size: 15px;
font-weight: 700;
display: flex;
align-items: center;
gap: 6px;
}

.column-title.completed { color: #16a34a; }
.column-title.in-progress { color: #0066cc; }
.column-title.next-up { color: #7c3aed; }

.column-count {
font-size: 13px;
color: #555555;
font-weight: 500;
}

/* Task Cards */
.task-list {
flex: 1;
}

.task-card {
background: #ffffff;
border: 1.5px solid #e5e7eb;
border-radius: 4px;
padding: 12px;
margin-bottom: 10px;
font-size: 13px;
}

.task-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 8px;
}

.task-name {
font-weight: 600;
color: #1a1a1a;
flex: 1;
line-height: 1.3;
font-size: 14px;
}

.project-tag {
font-size: 11px;
color: #555555;
background: #f3f4f6;
padding: 2px 8px;
border-radius: 10px;
white-space: nowrap;
margin-left: 8px;
font-weight: 600;
}

/* Subtasks */
.subtasks {
margin-left: 15px;
}

.subtask {
font-size: 12px;
color: #333333;
margin-bottom: 4px;
display: flex;
align-items: center;
gap: 6px;
font-weight: 500;
}

.check-icon {
width: 12px;
height: 12px;
border: 1.5px solid #d1d5db;
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}

.check-icon.done {
background: #16a34a;
border-color: #16a34a;
}

.check-icon.done::after {
content: '✓';
color: white;
font-size: 9px;
line-height: 1;
font-weight: bold;
}

.subtask-text {
flex: 1;
}

.progress {
font-size: 11px;
font-weight: 700;
color: #0066cc;
margin-left: auto;
}

/* Right Sidebar */
.sidebar {
width: 260px;
display: flex;
flex-direction: column;
gap: 15px;
}

/* KPI Box - Minimal */
.kpi-box {
background: #f9fafb;
border: 1.5px solid #e5e7eb;
border-radius: 4px;
padding: 12px;
}

.kpi-title {
font-size: 12px;
font-weight: 700;
color: #555555;
text-transform: uppercase;
margin-bottom: 8px;
letter-spacing: 0.5px;
}

.kpi-row {
font-size: 13px;
color: #1a1a1a;
margin-bottom: 5px;
display: flex;
align-items: baseline;
gap: 5px;
font-weight: 500;
}

.kpi-period {
font-weight: 700;
min-width: 50px;
}

.kpi-values {
color: #333333;
}

.kpi-value {
font-weight: 700;
}

.kpi-value.done {
color: #16a34a;
}

.kpi-value.wip {
color: #0066cc;
}

.kpi-value.blocked {
color: #dc2626;
}

/* Info Box */
.info-box {
background: #ffffff;
border: 1.5px solid #e5e7eb;
border-radius: 4px;
padding: 15px;
}

.info-box.highlights {
border-left: 3px solid #16a34a;
}

.info-box.risks {
border-left: 3px solid #dc2626;
}

.box-title {
font-size: 14px;
font-weight: 700;
margin-bottom: 10px;
color: #1a1a1a;
}

.info-item {
font-size: 12px;
color: #333333;
margin-bottom: 8px;
padding-left: 12px;
position: relative;
line-height: 1.4;
font-weight: 500;
}

.info-item::before {
content: '•';
position: absolute;
left: 0;
font-weight: bold;
font-size: 14px;
}

.highlights .info-item::before { color: #16a34a; }
.risks .info-item::before { color: #dc2626; }

.priority {
display: inline-block;
font-size: 10px;
padding: 1px 5px;
border-radius: 8px;
background: #fee2e2;
color: #dc2626;
font-weight: 700;
margin-left: 4px;
}

/* Data Input Section */
.data-input-section {
max-width: 1200px;
margin: 0 auto 30px;
background: white;
padding: 20px;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}

.input-title {
font-size: 20px;
font-weight: 700;
margin-bottom: 15px;
color: #1a1a1a;
}

.input-instructions {
font-size: 14px;
color: #555555;
margin-bottom: 15px;
line-height: 1.5;
}

textarea {
width: 100%;
height: 300px;
padding: 10px;
border: 1.5px solid #e5e7eb;
border-radius: 4px;
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
font-size: 12px;
resize: vertical;
}

.button-group {
margin-top: 15px;
display: flex;
gap: 10px;
}

button {
padding: 8px 20px;
border: none;
border-radius: 4px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}

.generate-btn {
background: #0066cc;
color: white;
}

.generate-btn:hover {
background: #0052a3;
}

.example-btn {
background: #f3f4f6;
color: #333333;
border: 1.5px solid #e5e7eb;
}

.example-btn:hover {
background: #e5e7eb;
}

.download-btn {
background: #16a34a;
color: white;
}

.download-btn:hover {
background: #15803d;
}

.error-message {
color: #dc2626;
font-size: 13px;
margin-top: 10px;
display: none;
font-weight: 600;
}

/* Export controls */
.export-controls {
max-width: 1200px;
margin: 20px auto;
text-align: center;
}

.export-info {
font-size: 12px;
color: #666;
margin-top: 8px;
}
</style>
</head>
<body>
<!-- Data Input Section -->
<div class="data-input-section">
<h2 class="input-title">Weekly Status Report Generator</h2>
<p class="input-instructions">
Paste your JSON data below and click "Generate Report". The JSON should include:
date, KPIs (week, month, year), completed tasks, in-progress tasks, next-up tasks, highlights, and risks.
</p>
<textarea id="jsonInput" placeholder="Paste your JSON data here..."></textarea>
<div class="button-group">
<button class="generate-btn" onclick="generateReport()">Generate Report</button>
<button class="example-btn" onclick="loadExample()">Load Example Data</button>
</div>
<div class="error-message" id="errorMessage"></div>
</div>

<!-- Export Controls (Hidden initially) -->
<div class="export-controls" id="exportControls" style="display: none;">
<button class="download-btn" onclick="downloadAsImage()">Download as High-Resolution Image</button>
<div class="export-info">The image will capture the entire report at 2x resolution</div>
</div>

<!-- Report Container (Hidden initially) -->
<div class="report-container" id="reportContainer" style="display: none;">
<!-- Content will be generated here -->
</div>

<script>
// Example data structure
const exampleData = {
"date": "January 20-24, 2025",
"kpi": {
"week": {
"completed": 18,
"inProgress": 12,
"blocked": 3
},
"month": {
"completed": 76
},
"year": {
"completed": 76
}
},
"completedTasks": [
{
"name": "User Authentication",
"project": "Mobile App",
"subtasks": [
{"name": "OAuth integration", "done": true},
{"name": "2FA setup", "done": true},
{"name": "Session handling", "done": true}
]
},
{
"name": "Payment Setup",
"project": "Payment",
"subtasks": [
{"name": "Stripe integration", "done": true},
{"name": "Webhook config", "done": true}
]
},
{
"name": "Database Schema",
"project": "DB Migration",
"subtasks": [
{"name": "Schema design", "done": true},
{"name": "Indexes created", "done": true}
]
}
],
"inProgressTasks": [
{
"name": "Dashboard UI",
"project": "Mobile App",
"subtasks": [
{"name": "Layout complete", "done": true},
{"name": "Charts integration", "done": false, "progress": 60},
{"name": "Real-time sync", "done": false}
]
},
{
"name": "Performance Testing",
"project": "DB Migration",
"subtasks": [
{"name": "Test env ready", "done": true},
{"name": "Load testing", "done": false, "progress": 40},
{"name": "Optimization", "done": false}
]
},
{
"name": "iOS Release",
"project": "Mobile App",
"subtasks": [
{"name": "Build prepared", "done": true},
{"name": "App Store submit", "done": false, "progress": 90}
]
}
],
"nextUpTasks": [
{
"name": "Android Development",
"project": "Mobile App",
"subtasks": [
{"name": "Environment setup", "done": false},
{"name": "Core features", "done": false},
{"name": "Testing", "done": false}
]
},
{
"name": "Analytics Integration",
"project": "Analytics",
"subtasks": [
{"name": "Google Analytics", "done": false},
{"name": "Custom events", "done": false}
]
},
{
"name": "User Feedback System",
"project": "Features",
"subtasks": [
{"name": "Feedback form", "done": false},
{"name": "Review system", "done": false}
]
}
],
"highlights": [
"Auth system deployed successfully",
"Zero production incidents",
"Mobile app 75% complete",
"Team ahead of schedule"
],
"risks": [
{"text": "iOS approval delayed", "priority": "HIGH"},
{"text": "Payment API rate limits", "priority": null},
{"text": "QA resources needed", "priority": null}
]
};

function loadExample() {
document.getElementById('jsonInput').value = JSON.stringify(exampleData, null, 2);
}

function generateReport() {
const errorEl = document.getElementById('errorMessage');
errorEl.style.display = 'none';

try {
const jsonText = document.getElementById('jsonInput').value;
if (!jsonText.trim()) {
throw new Error('Please enter JSON data');
}

const data = JSON.parse(jsonText);

// Validate required fields
if (!data.date || !data.kpi || !data.completedTasks || !data.inProgressTasks) {
throw new Error('Missing required fields. Please check the example data structure.');
}

// If nextUpTasks is missing, create empty array
if (!data.nextUpTasks) {
data.nextUpTasks = [];
}

// Generate the report HTML
const reportHTML = generateReportHTML(data);

// Display the report
const reportContainer = document.getElementById('reportContainer');
reportContainer.innerHTML = reportHTML;
reportContainer.style.display = 'block';

// Show export controls
document.getElementById('exportControls').style.display = 'block';

// Scroll to report
reportContainer.scrollIntoView({ behavior: 'smooth' });

} catch (error) {
errorEl.textContent = 'Error: ' + error.message;
errorEl.style.display = 'block';
}
}

function generateReportHTML(data) {
const completedCount = data.completedTasks.length;
const inProgressCount = data.inProgressTasks.length;
const nextUpCount = data.nextUpTasks ? data.nextUpTasks.length : 0;

return `
<!-- Header -->
<div class="header">
<div class="title">Weekly Status Report</div>
<div class="date">${data.date}</div>
</div>

<!-- Main Layout -->
<div class="main-layout">
<!-- Task Columns -->
<div class="tasks-container">
<!-- Completed Column -->
<div class="task-column">
<div class="column-header">
<div class="column-title completed">
✓ Completed
</div>
<span class="column-count">${completedCount} tasks</span>
</div>

<div class="task-list">
${generateTaskCards(data.completedTasks, true)}
</div>
</div>

<!-- In Progress Column -->
<div class="task-column">
<div class="column-header">
<div class="column-title in-progress">
⟳ In Progress
</div>
<span class="column-count">${inProgressCount} tasks</span>
</div>

<div class="task-list">
${generateTaskCards(data.inProgressTasks, false)}
</div>
</div>

<!-- Next Up Column -->
<div class="task-column">
<div class="column-header">
<div class="column-title next-up">
⟶ Next Up
</div>
<span class="column-count">${nextUpCount} tasks</span>
</div>

<div class="task-list">
${data.nextUpTasks && data.nextUpTasks.length > 0 ?
generateTaskCards(data.nextUpTasks, false) :
'<div style="color: #999; font-size: 12px; text-align: center; padding: 20px;">No upcoming tasks</div>'}
</div>
</div>
</div>

<!-- Right Sidebar -->
<div class="sidebar">
<!-- KPIs - Minimal -->
<div class="kpi-box">
<div class="kpi-title">Key Metrics</div>
<div class="kpi-row">
<span class="kpi-period">Week:</span>
<span class="kpi-values">
<span class="kpi-value done">${data.kpi.week.completed}</span> done,
<span class="kpi-value wip">${data.kpi.week.inProgress}</span> wip,
<span class="kpi-value blocked">${data.kpi.week.blocked}</span> blocked
</span>
</div>
<div class="kpi-row">
<span class="kpi-period">Month:</span>
<span class="kpi-values"><span class="kpi-value">${data.kpi.month.completed}</span> completed</span>
</div>
<div class="kpi-row">
<span class="kpi-period">Year:</span>
<span class="kpi-values"><span class="kpi-value">${data.kpi.year.completed}</span> completed</span>
</div>
</div>

<!-- Highlights -->
<div class="info-box highlights">
<div class="box-title">Weekly Highlights</div>
${data.highlights.map(item => `
<div class="info-item">${item}</div>
`).join('')}
</div>

<!-- Risks -->
<div class="info-box risks">
<div class="box-title">Risks & Blockers</div>
${data.risks.map(risk => `
<div class="info-item">
${risk.text}
${risk.priority ? `<span class="priority">${risk.priority}</span>` : ''}
</div>
`).join('')}
</div>
</div>
</div>
`;
}

function generateTaskCards(tasks, isCompleted) {
return tasks.map(task => `
<div class="task-card">
<div class="task-header">
<span class="task-name">${task.name}</span>
<span class="project-tag">${task.project}</span>
</div>
<div class="subtasks">
${task.subtasks.map(subtask => `
<div class="subtask">
<div class="check-icon ${subtask.done ? 'done' : ''}"></div>
<span class="subtask-text">${subtask.name}</span>
${subtask.progress ? `<span class="progress">${subtask.progress}%</span>` : ''}
</div>
`).join('')}
</div>
</div>
`).join('');
}

function downloadAsImage() {
const element = document.getElementById('reportContainer');
const downloadBtn = document.querySelector('.download-btn');

// Update button to show loading state
downloadBtn.textContent = 'Generating image...';
downloadBtn.disabled = true;

// Get the full height of the content
const fullHeight = element.scrollHeight;
const fullWidth = element.scrollWidth;

// Configure html2canvas for high quality and full capture
html2canvas(element, {
scale: 2, // 2x resolution for high quality
useCORS: true,
logging: false,
backgroundColor: '#ffffff',
width: fullWidth,
height: fullHeight,
windowWidth: fullWidth,
windowHeight: fullHeight,
scrollX: 0,
scrollY: 0
}).then(canvas => {
// Convert to blob and download
canvas.toBlob(function(blob) {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `weekly-status-report-${new Date().toISOString().split('T')[0]}.png`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);

// Reset button
downloadBtn.textContent = 'Download as High-Resolution Image';
downloadBtn.disabled = false;
}, 'image/png', 1.0);
}).catch(error => {
console.error('Error generating image:', error);
alert('Error generating image. Please try again.');

// Reset button
downloadBtn.textContent = 'Download as High-Resolution Image';
downloadBtn.disabled = false;
});
}
</script>
</body>
</html>