tasks-and-bugs / index.html
salmanarshad's picture
Add 3 files
f5fae77 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dev Workflow Tracker</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.gradient-bg {
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
}
.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);
}
.transition-all {
transition: all 0.3s ease;
}
.command-box {
font-family: 'Courier New', monospace;
background-color: #2d3748;
color: #f7fafc;
}
.status-pending {
background-color: #f6e05e;
color: #975a16;
}
.status-success {
background-color: #68d391;
color: #276749;
}
.status-failed {
background-color: #fc8181;
color: #9b2c2c;
}
.status-merged {
background-color: #63b3ed;
color: #2c5282;
}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="gradient-bg text-white py-6 shadow-lg">
<div class="container mx-auto px-4">
<div class="flex justify-between items-center">
<h1 class="text-3xl font-bold flex items-center">
<i class="fas fa-code-branch mr-3"></i> Dev Workflow Tracker
</h1>
<button id="newTaskBtn" class="bg-white text-blue-800 px-4 py-2 rounded-lg font-semibold hover:bg-blue-100 transition-all">
<i class="fas fa-plus mr-2"></i> New Task
</button>
</div>
</div>
</div>
<div class="container mx-auto px-4 py-8">
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<!-- Tasks Column -->
<div class="bg-white rounded-lg shadow-md p-6">
<div class="flex justify-between items-center mb-6">
<h2 class="text-xl font-semibold text-gray-800">
<i class="fas fa-tasks mr-2 text-blue-500"></i> Active Tasks
</h2>
<span class="bg-blue-100 text-blue-800 px-3 py-1 rounded-full text-sm font-medium">
<span id="taskCount">0</span> tasks
</span>
</div>
<div id="tasksContainer" class="space-y-4">
<!-- Tasks will be added here dynamically -->
</div>
</div>
<!-- Merge Status Column -->
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-6">
<i class="fas fa-code-merge mr-2 text-green-500"></i> Merge Status
</h2>
<div id="mergeStatusContainer" class="space-y-4">
<!-- Merge status will be added here dynamically -->
</div>
</div>
<!-- Commands Column -->
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-6">
<i class="fas fa-terminal mr-2 text-purple-500"></i> Common Commands
</h2>
<div class="space-y-4">
<div class="command-box p-4 rounded-lg">
<div class="flex justify-between items-center mb-2">
<span class="font-semibold">Git Branch</span>
<button class="copy-btn text-blue-400 hover:text-blue-300" data-clipboard-text="git checkout -b feature/your-feature">
<i class="far fa-copy"></i>
</button>
</div>
<code>git checkout -b feature/your-feature</code>
</div>
<div class="command-box p-4 rounded-lg">
<div class="flex justify-between items-center mb-2">
<span class="font-semibold">Rebase</span>
<button class="copy-btn text-blue-400 hover:text-blue-300" data-clipboard-text="git pull --rebase origin dev">
<i class="far fa-copy"></i>
</button>
</div>
<code>git pull --rebase origin dev</code>
</div>
<div class="command-box p-4 rounded-lg">
<div class="flex justify-between items-center mb-2">
<span class="font-semibold">Merge</span>
<button class="copy-btn text-blue-400 hover:text-blue-300" data-clipboard-text="git merge --no-ff feature/your-feature">
<i class="far fa-copy"></i>
</button>
</div>
<code>git merge --no-ff feature/your-feature</code>
</div>
</div>
</div>
</div>
</div>
<!-- New Task Modal -->
<div id="taskModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden">
<div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4">
<div class="gradient-bg text-white rounded-t-lg px-6 py-4">
<h2 class="text-xl font-bold">Add New Task</h2>
</div>
<div class="p-6">
<form id="taskForm">
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-2" for="taskType">Task Type</label>
<select id="taskType" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="feature">Feature</option>
<option value="bug">Bug</option>
<option value="hotfix">Hotfix</option>
<option value="refactor">Refactor</option>
</select>
</div>
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-2" for="taskTitle">Title</label>
<input type="text" id="taskTitle" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="What are you working on?">
</div>
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-2" for="branchName">Branch Name</label>
<input type="text" id="branchName" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="feature/your-feature">
</div>
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-2">Affected Services</label>
<div class="space-y-2">
<div class="flex items-center">
<input type="checkbox" id="service1" class="mr-2" checked>
<label for="service1">Frontend</label>
</div>
<div class="flex items-center">
<input type="checkbox" id="service2" class="mr-2">
<label for="service2">User Service</label>
</div>
<div class="flex items-center">
<input type="checkbox" id="service3" class="mr-2">
<label for="service3">Payment Service</label>
</div>
<div class="flex items-center">
<input type="checkbox" id="service4" class="mr-2">
<label for="service4">Order Service</label>
</div>
</div>
</div>
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-2" for="mergeCommands">Merge Commands</label>
<textarea id="mergeCommands" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" rows="3" placeholder="Any special merge commands or notes"></textarea>
</div>
<div class="flex justify-end space-x-3">
<button type="button" id="cancelTaskBtn" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-100">Cancel</button>
<button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">Add Task</button>
</div>
</form>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Sample data for demonstration
const sampleTasks = [
{
id: 1,
type: 'feature',
title: 'Add user profile page',
branch: 'feature/user-profile',
services: ['Frontend', 'User Service'],
commands: 'git pull --rebase origin dev\nnpm run test\n',
devStatus: 'pending',
masterStatus: 'pending'
},
{
id: 2,
type: 'bug',
title: 'Fix payment validation',
branch: 'bugfix/payment-validation',
services: ['Payment Service'],
commands: 'git fetch origin\n',
devStatus: 'merged',
masterStatus: 'pending'
}
];
let tasks = JSON.parse(localStorage.getItem('devTasks')) || sampleTasks;
// DOM elements
const tasksContainer = document.getElementById('tasksContainer');
const mergeStatusContainer = document.getElementById('mergeStatusContainer');
const taskModal = document.getElementById('taskModal');
const newTaskBtn = document.getElementById('newTaskBtn');
const cancelTaskBtn = document.getElementById('cancelTaskBtn');
const taskForm = document.getElementById('taskForm');
const taskCount = document.getElementById('taskCount');
// Initialize clipboard functionality
document.querySelectorAll('.copy-btn').forEach(btn => {
btn.addEventListener('click', function() {
const text = this.getAttribute('data-clipboard-text');
navigator.clipboard.writeText(text).then(() => {
const originalIcon = this.innerHTML;
this.innerHTML = '<i class="fas fa-check"></i>';
setTimeout(() => {
this.innerHTML = originalIcon;
}, 2000);
});
});
});
// Modal controls
newTaskBtn.addEventListener('click', () => {
taskModal.classList.remove('hidden');
});
cancelTaskBtn.addEventListener('click', () => {
taskModal.classList.add('hidden');
});
// Form submission
taskForm.addEventListener('submit', function(e) {
e.preventDefault();
const type = document.getElementById('taskType').value;
const title = document.getElementById('taskTitle').value.trim();
const branch = document.getElementById('branchName').value.trim();
if (!title || !branch) {
alert('Please fill in all required fields');
return;
}
// Get selected services
const services = [];
document.querySelectorAll('input[type="checkbox"]:checked').forEach(checkbox => {
services.push(checkbox.nextElementSibling.textContent);
});
const commands = document.getElementById('mergeCommands').value.trim();
const newTask = {
id: Date.now(),
type,
title,
branch,
services,
commands,
devStatus: 'pending',
masterStatus: 'pending'
};
tasks.push(newTask);
saveTasks();
renderTasks();
renderMergeStatus();
taskModal.classList.add('hidden');
taskForm.reset();
});
// Save tasks to localStorage
function saveTasks() {
localStorage.setItem('devTasks', JSON.stringify(tasks));
updateTaskCount();
}
// Update task count
function updateTaskCount() {
taskCount.textContent = tasks.length;
}
// Render tasks
function renderTasks() {
tasksContainer.innerHTML = '';
tasks.forEach(task => {
const taskElement = document.createElement('div');
taskElement.className = 'bg-white border border-gray-200 rounded-lg p-4 shadow-sm card-hover transition-all';
const typeIcon = task.type === 'feature' ? 'fa-star' :
task.type === 'bug' ? 'fa-bug' :
task.type === 'hotfix' ? 'fa-fire' : 'fa-code';
taskElement.innerHTML = `
<div class="flex justify-between items-start mb-2">
<div class="flex items-center">
<i class="fas ${typeIcon} mr-2 ${task.type === 'feature' ? 'text-blue-500' : 'text-red-500'}"></i>
<h3 class="font-semibold text-gray-800">${task.title}</h3>
</div>
<button class="delete-task text-gray-400 hover:text-red-500" data-id="${task.id}">
<i class="fas fa-times"></i>
</button>
</div>
<div class="mb-2">
<span class="text-sm font-medium text-gray-600">Branch:</span>
<span class="text-sm ml-2 font-mono bg-gray-100 px-2 py-1 rounded">${task.branch}</span>
</div>
<div class="mb-3">
<span class="text-sm font-medium text-gray-600">Services:</span>
<div class="flex flex-wrap gap-1 mt-1">
${task.services.map(service => `
<span class="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">${service}</span>
`).join('')}
</div>
</div>
${task.commands ? `
<div class="mb-3">
<span class="text-sm font-medium text-gray-600">Commands:</span>
<div class="command-box p-2 rounded mt-1 text-xs">
<pre>${task.commands}</pre>
</div>
</div>
` : ''}
<div class="flex justify-between items-center pt-2 border-t border-gray-100">
<div>
<span class="text-xs text-gray-500">Created: ${new Date(task.id).toLocaleDateString()}</span>
</div>
<div class="flex space-x-2">
<button class="edit-task text-blue-500 hover:text-blue-700 text-sm" data-id="${task.id}">
<i class="fas fa-edit mr-1"></i>Edit
</button>
<button class="update-status text-green-500 hover:text-green-700 text-sm" data-id="${task.id}">
<i class="fas fa-sync-alt mr-1"></i>Update Status
</button>
</div>
</div>
`;
tasksContainer.appendChild(taskElement);
});
// Add event listeners for delete buttons
document.querySelectorAll('.delete-task').forEach(btn => {
btn.addEventListener('click', function() {
const taskId = parseInt(this.getAttribute('data-id'));
tasks = tasks.filter(task => task.id !== taskId);
saveTasks();
renderTasks();
renderMergeStatus();
});
});
// Add event listeners for edit buttons
document.querySelectorAll('.edit-task').forEach(btn => {
btn.addEventListener('click', function() {
const taskId = parseInt(this.getAttribute('data-id'));
const task = tasks.find(t => t.id === taskId);
if (task) {
// Populate the form with task data
document.getElementById('taskType').value = task.type;
document.getElementById('taskTitle').value = task.title;
document.getElementById('branchName').value = task.branch;
document.getElementById('mergeCommands').value = task.commands || '';
// Check the appropriate service checkboxes
document.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
const serviceName = checkbox.nextElementSibling.textContent;
checkbox.checked = task.services.includes(serviceName);
});
// Show the modal
taskModal.classList.remove('hidden');
// Remove the task from the array when saved
taskForm.onsubmit = function(e) {
e.preventDefault();
const type = document.getElementById('taskType').value;
const title = document.getElementById('taskTitle').value.trim();
const branch = document.getElementById('branchName').value.trim();
if (!title || !branch) {
alert('Please fill in all required fields');
return;
}
// Get selected services
const services = [];
document.querySelectorAll('input[type="checkbox"]:checked').forEach(checkbox => {
services.push(checkbox.nextElementSibling.textContent);
});
const commands = document.getElementById('mergeCommands').value.trim();
// Update the task
const updatedTask = {
...task,
type,
title,
branch,
services,
commands
};
tasks = tasks.map(t => t.id === task.id ? updatedTask : t);
saveTasks();
renderTasks();
renderMergeStatus();
taskModal.classList.add('hidden');
taskForm.reset();
taskForm.onsubmit = originalSubmitHandler;
};
}
});
});
// Add event listeners for status update buttons
document.querySelectorAll('.update-status').forEach(btn => {
btn.addEventListener('click', function() {
const taskId = parseInt(this.getAttribute('data-id'));
const task = tasks.find(t => t.id === taskId);
if (task) {
if (task.devStatus === 'pending') {
task.devStatus = 'merged';
} else if (task.masterStatus === 'pending') {
task.masterStatus = 'merged';
}
saveTasks();
renderTasks();
renderMergeStatus();
}
});
});
updateTaskCount();
}
// Render merge status
function renderMergeStatus() {
mergeStatusContainer.innerHTML = '';
tasks.forEach(task => {
const statusElement = document.createElement('div');
statusElement.className = 'bg-white border border-gray-200 rounded-lg p-4 shadow-sm card-hover transition-all';
statusElement.innerHTML = `
<div class="flex justify-between items-start mb-2">
<h3 class="font-semibold text-gray-800">${task.title}</h3>
<span class="text-xs font-mono bg-gray-100 px-2 py-1 rounded">${task.branch}</span>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="p-2 rounded text-center">
<div class="text-xs font-medium text-gray-600 mb-1">DEV</div>
<span class="px-3 py-1 rounded-full text-xs font-medium status-${task.devStatus}">
${task.devStatus === 'pending' ? 'Pending' : 'Merged'}
</span>
</div>
<div class="p-2 rounded text-center">
<div class="text-xs font-medium text-gray-600 mb-1">MASTER</div>
<span class="px-3 py-1 rounded-full text-xs font-medium status-${task.masterStatus}">
${task.masterStatus === 'pending' ? 'Pending' : 'Merged'}
</span>
</div>
</div>
${task.services.length > 0 ? `
<div class="mt-3 pt-2 border-t border-gray-100">
<div class="text-xs font-medium text-gray-600 mb-1">Services:</div>
<div class="flex flex-wrap gap-1">
${task.services.map(service => `
<span class="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">${service}</span>
`).join('')}
</div>
</div>
` : ''}
`;
mergeStatusContainer.appendChild(statusElement);
});
}
// Store the original form submit handler
const originalSubmitHandler = taskForm.onsubmit;
// Initial render
renderTasks();
renderMergeStatus();
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=salmanarshad/tasks-and-bugs" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>