manyone-space / index.html
manyone's picture
Add 2 files
39524b7 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Task Manager</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>
.kanban-column {
min-height: 300px;
background-color: #f3f4f6;
border-radius: 0.5rem;
padding: 1rem;
}
.task-card {
transition: all 0.3s ease;
cursor: grab;
}
.task-card:active {
cursor: grabbing;
}
.task-card.dragging {
opacity: 0.5;
transform: scale(1.05);
}
.kanban-column.highlight {
background-color: #e5e7eb;
}
.view-option.active {
background-color: #3b82f6;
color: white;
}
.category-work { border-left: 4px solid #3b82f6; }
.category-errand { border-left: 4px solid #10b981; }
.category-home { border-left: 4px solid #f59e0b; }
.category-social { border-left: 4px solid #8b5cf6; }
.status-todo { background-color: #f3f4f6; }
.status-in-progress { background-color: #bfdbfe; }
.status-done { background-color: #dcfce7; }
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="container mx-auto px-4 py-8">
<header class="mb-8">
<h1 class="text-3xl font-bold text-gray-800">Task Manager</h1>
<p class="text-gray-600">Organize your tasks efficiently</p>
</header>
<div class="bg-white rounded-lg shadow-md p-6 mb-6">
<div class="flex justify-between items-center mb-6">
<div class="flex space-x-2">
<button id="list-view-btn" class="view-option active px-4 py-2 rounded-lg border border-gray-300">
<i class="fas fa-list mr-2"></i>List View
</button>
<button id="kanban-view-btn" class="view-option px-4 py-2 rounded-lg border border-gray-300">
<i class="fas fa-columns mr-2"></i>Kanban Board
</button>
<button id="card-view-btn" class="view-option px-4 py-2 rounded-lg border border-gray-300">
<i class="fas fa-grip mr-2"></i>Card View
</button>
</div>
<button id="add-task-btn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg">
<i class="fas fa-plus mr-2"></i>Add Task
</button>
</div>
<div class="mb-6 flex flex-wrap gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Filter by Category</label>
<select id="category-filter" class="border border-gray-300 rounded-md px-3 py-2 w-full">
<option value="all">All Categories</option>
<option value="work">Work</option>
<option value="errand">Errand</option>
<option value="home">Home</option>
<option value="social">Social</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Filter by Status</label>
<select id="status-filter" class="border border-gray-300 rounded-md px-3 py-2 w-full">
<option value="all">All Statuses</option>
<option value="to-do">To Do</option>
<option value="in-progress">In Progress</option>
<option value="done">Done</option>
</select>
</div>
<div id="kanban-grouping-controls" class="hidden">
<label class="block text-sm font-medium text-gray-700 mb-1">Group By</label>
<select id="kanban-group-by" class="border border-gray-300 rounded-md px-3 py-2 w-full">
<option value="status">Status</option>
<option value="category">Category</option>
</select>
</div>
</div>
<!-- List View -->
<div id="list-view" class="view-content">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Task</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Category</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody id="task-list" class="bg-white divide-y divide-gray-200">
<!-- Tasks will be populated here -->
</tbody>
</table>
</div>
<!-- Kanban View -->
<div id="kanban-view" class="view-content hidden">
<div id="kanban-board" class="grid grid-cols-1 md:grid-cols-3 gap-4">
<!-- Kanban columns will be populated here -->
</div>
</div>
<!-- Card View -->
<div id="card-view" class="view-content hidden">
<div id="card-container" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
<!-- Cards will be populated here -->
</div>
</div>
</div>
</div>
<!-- Add/Edit Task Modal -->
<div id="task-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50">
<div class="bg-white rounded-lg shadow-xl p-6 w-full max-w-md">
<div class="flex justify-between items-center mb-4">
<h3 id="modal-title" class="text-xl font-semibold text-gray-800">Add New Task</h3>
<button id="close-modal" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<form id="task-form">
<input type="hidden" id="task-id">
<div class="mb-4">
<label for="task-description" class="block text-sm font-medium text-gray-700 mb-1">Description</label>
<textarea id="task-description" rows="3" class="border border-gray-300 rounded-md px-3 py-2 w-full" required></textarea>
</div>
<div class="grid grid-cols-2 gap-4 mb-4">
<div>
<label for="task-category" class="block text-sm font-medium text-gray-700 mb-1">Category</label>
<select id="task-category" class="border border-gray-300 rounded-md px-3 py-2 w-full" required>
<option value="work">Work</option>
<option value="errand">Errand</option>
<option value="home">Home</option>
<option value="social">Social</option>
</select>
</div>
<div>
<label for="task-status" class="block text-sm font-medium text-gray-700 mb-1">Status</label>
<select id="task-status" class="border border-gray-300 rounded-md px-3 py-2 w-full" required>
<option value="to-do">To Do</option>
<option value="in-progress">In Progress</option>
<option value="done">Done</option>
</select>
</div>
</div>
<div class="flex justify-end space-x-3">
<button type="button" id="cancel-task" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50">
Cancel
</button>
<button type="submit" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600">
Save Task
</button>
</div>
</form>
</div>
</div>
<script>
// Sample initial tasks
let tasks = [
{ id: 1, description: "Complete project report", category: "work", status: "in-progress" },
{ id: 2, description: "Buy groceries", category: "errand", status: "to-do" },
{ id: 3, description: "Call mom", category: "social", status: "to-do" },
{ id: 4, description: "Fix leaking faucet", category: "home", status: "done" },
{ id: 5, description: "Prepare presentation for meeting", category: "work", status: "to-do" },
{ id: 6, description: "Go to the gym", category: "social", status: "in-progress" },
{ id: 7, description: "Pay electricity bill", category: "errand", status: "to-do" },
{ id: 8, description: "Organize closet", category: "home", status: "to-do" },
{ id: 9, description: "Team lunch", category: "social", status: "done" },
{ id: 10, description: "Review code changes", category: "work", status: "in-progress" },
{ id: 11, description: "Pick up dry cleaning", category: "errand", status: "done" },
{ id: 12, description: "Water plants", category: "home", status: "to-do" }
];
// DOM elements
const listView = document.getElementById('list-view');
const kanbanView = document.getElementById('kanban-view');
const cardView = document.getElementById('card-view');
const listViewBtn = document.getElementById('list-view-btn');
const kanbanViewBtn = document.getElementById('kanban-view-btn');
const cardViewBtn = document.getElementById('card-view-btn');
const taskList = document.getElementById('task-list');
const kanbanBoard = document.getElementById('kanban-board');
const cardContainer = document.getElementById('card-container');
const addTaskBtn = document.getElementById('add-task-btn');
const taskModal = document.getElementById('task-modal');
const closeModal = document.getElementById('close-modal');
const cancelTask = document.getElementById('cancel-task');
const taskForm = document.getElementById('task-form');
const modalTitle = document.getElementById('modal-title');
const taskIdInput = document.getElementById('task-id');
const taskDescription = document.getElementById('task-description');
const taskCategory = document.getElementById('task-category');
const taskStatus = document.getElementById('task-status');
const categoryFilter = document.getElementById('category-filter');
const statusFilter = document.getElementById('status-filter');
const kanbanGroupingControls = document.getElementById('kanban-grouping-controls');
const kanbanGroupBy = document.getElementById('kanban-group-by');
// View management
function showView(view) {
document.querySelectorAll('.view-content').forEach(v => v.classList.add('hidden'));
document.querySelectorAll('.view-option').forEach(btn => btn.classList.remove('active'));
view.classList.remove('hidden');
if (view === listView) {
listViewBtn.classList.add('active');
kanbanGroupingControls.classList.add('hidden');
renderListView();
} else if (view === kanbanView) {
kanbanViewBtn.classList.add('active');
kanbanGroupingControls.classList.remove('hidden');
renderKanbanView();
} else if (view === cardView) {
cardViewBtn.classList.add('active');
kanbanGroupingControls.classList.add('hidden');
renderCardView();
}
}
// Render functions
function renderListView() {
const filteredTasks = filterTasks();
taskList.innerHTML = '';
filteredTasks.forEach(task => {
const row = document.createElement('tr');
row.className = `status-${task.status.replace(' ', '-')}`;
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">${task.description}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full
${getCategoryColorClass(task.category)}">
${capitalizeFirstLetter(task.category)}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full
${getStatusColorClass(task.status)}">
${capitalizeFirstLetter(task.status.replace('-', ' '))}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button class="edit-task text-blue-600 hover:text-blue-900 mr-3" data-id="${task.id}">
<i class="fas fa-edit"></i>
</button>
<button class="delete-task text-red-600 hover:text-red-900" data-id="${task.id}">
<i class="fas fa-trash"></i>
</button>
</td>
`;
taskList.appendChild(row);
});
// Add event listeners to edit and delete buttons
document.querySelectorAll('.edit-task').forEach(btn => {
btn.addEventListener('click', (e) => editTask(e.target.closest('button').dataset.id));
});
document.querySelectorAll('.delete-task').forEach(btn => {
btn.addEventListener('click', (e) => deleteTask(e.target.closest('button').dataset.id));
});
}
function renderKanbanView() {
const groupBy = kanbanGroupBy.value;
const filteredTasks = filterTasks();
// Define groups based on selection
let groups = [];
if (groupBy === 'status') {
groups = ['to-do', 'in-progress', 'done'];
} else {
groups = ['work', 'errand', 'home', 'social'];
}
kanbanBoard.innerHTML = '';
groups.forEach(group => {
const columnTasks = filteredTasks.filter(task =>
groupBy === 'status' ? task.status === group : task.category === group
);
const column = document.createElement('div');
column.className = 'kanban-column';
column.dataset.group = group;
const columnTitle = document.createElement('h3');
columnTitle.className = 'text-lg font-semibold mb-4';
columnTitle.textContent = capitalizeFirstLetter(group.replace('-', ' '));
column.appendChild(columnTitle);
const tasksContainer = document.createElement('div');
tasksContainer.className = 'space-y-3';
columnTasks.forEach(task => {
const taskCard = document.createElement('div');
taskCard.className = `task-card p-3 rounded-md shadow-sm bg-white ${groupBy === 'category' ? `category-${task.category}` : ''}`;
taskCard.draggable = true;
taskCard.dataset.id = task.id;
taskCard.innerHTML = `
<div class="text-sm mb-2">${task.description}</div>
<div class="flex justify-between items-center text-xs">
<span class="px-2 py-1 rounded-full ${groupBy === 'status' ? getCategoryColorClass(task.category) : getStatusColorClass(task.status)}">
${capitalizeFirstLetter(groupBy === 'status' ? task.category : task.status.replace('-', ' '))}
</span>
<div>
<button class="edit-task text-blue-600 hover:text-blue-900 mr-2" data-id="${task.id}">
<i class="fas fa-edit"></i>
</button>
<button class="delete-task text-red-600 hover:text-red-900" data-id="${task.id}">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
`;
tasksContainer.appendChild(taskCard);
});
column.appendChild(tasksContainer);
kanbanBoard.appendChild(column);
});
// Add event listeners to edit and delete buttons
document.querySelectorAll('.edit-task').forEach(btn => {
btn.addEventListener('click', (e) => editTask(e.target.closest('button').dataset.id));
});
document.querySelectorAll('.delete-task').forEach(btn => {
btn.addEventListener('click', (e) => deleteTask(e.target.closest('button').dataset.id));
});
// Setup drag and drop
setupDragAndDrop();
}
function renderCardView() {
const filteredTasks = filterTasks();
cardContainer.innerHTML = '';
filteredTasks.forEach(task => {
const card = document.createElement('div');
card.className = `task-card p-4 rounded-lg shadow-sm bg-white ${getStatusClass(task.status)} category-${task.category}`;
card.innerHTML = `
<div class="text-sm mb-3">${task.description}</div>
<div class="flex justify-between items-center text-xs">
<span class="px-2 py-1 rounded-full ${getCategoryColorClass(task.category)}">
${capitalizeFirstLetter(task.category)}
</span>
<span class="px-2 py-1 rounded-full ${getStatusColorClass(task.status)}">
${capitalizeFirstLetter(task.status.replace('-', ' '))}
</span>
</div>
<div class="mt-3 flex justify-end space-x-2">
<button class="edit-task text-blue-600 hover:text-blue-900" data-id="${task.id}">
<i class="fas fa-edit"></i>
</button>
<button class="delete-task text-red-600 hover:text-red-900" data-id="${task.id}">
<i class="fas fa-trash"></i>
</button>
</div>
`;
cardContainer.appendChild(card);
});
// Add event listeners to edit and delete buttons
document.querySelectorAll('.edit-task').forEach(btn => {
btn.addEventListener('click', (e) => editTask(e.target.closest('button').dataset.id));
});
document.querySelectorAll('.delete-task').forEach(btn => {
btn.addEventListener('click', (e) => deleteTask(e.target.closest('button').dataset.id));
});
}
// Task CRUD operations
function addTask(task) {
task.id = tasks.length > 0 ? Math.max(...tasks.map(t => t.id)) + 1 : 1;
tasks.push(task);
renderCurrentView();
}
function updateTask(updatedTask) {
const index = tasks.findIndex(t => t.id === parseInt(updatedTask.id));
if (index !== -1) {
tasks[index] = updatedTask;
renderCurrentView();
}
}
function deleteTask(id) {
if (confirm('Are you sure you want to delete this task?')) {
tasks = tasks.filter(task => task.id !== parseInt(id));
renderCurrentView();
}
}
function editTask(id) {
const task = tasks.find(t => t.id === parseInt(id));
if (task) {
modalTitle.textContent = 'Edit Task';
taskIdInput.value = task.id;
taskDescription.value = task.description;
taskCategory.value = task.category;
taskStatus.value = task.status;
showModal();
}
}
// Modal functions
function showModal() {
taskModal.classList.remove('hidden');
}
function hideModal() {
taskModal.classList.add('hidden');
taskForm.reset();
taskIdInput.value = '';
}
// Helper functions
function filterTasks() {
const category = categoryFilter.value;
const status = statusFilter.value;
return tasks.filter(task => {
const categoryMatch = category === 'all' || task.category === category;
const statusMatch = status === 'all' || task.status === status.replace(' ', '-');
return categoryMatch && statusMatch;
});
}
function getCategoryColorClass(category) {
switch(category) {
case 'work': return 'bg-blue-100 text-blue-800';
case 'errand': return 'bg-green-100 text-green-800';
case 'home': return 'bg-yellow-100 text-yellow-800';
case 'social': return 'bg-purple-100 text-purple-800';
default: return 'bg-gray-100 text-gray-800';
}
}
function getStatusColorClass(status) {
switch(status) {
case 'to-do': return 'bg-gray-100 text-gray-800';
case 'in-progress': return 'bg-blue-100 text-blue-800';
case 'done': return 'bg-green-100 text-green-800';
default: return 'bg-gray-100 text-gray-800';
}
}
function getStatusClass(status) {
return `status-${status.replace(' ', '-')}`;
}
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function renderCurrentView() {
if (!listView.classList.contains('hidden')) {
renderListView();
} else if (!kanbanView.classList.contains('hidden')) {
renderKanbanView();
} else if (!cardView.classList.contains('hidden')) {
renderCardView();
}
}
// Drag and drop functionality
function setupDragAndDrop() {
const taskCards = document.querySelectorAll('.task-card');
const kanbanColumns = document.querySelectorAll('.kanban-column');
let draggedItem = null;
taskCards.forEach(card => {
card.addEventListener('dragstart', (e) => {
draggedItem = card;
setTimeout(() => {
card.classList.add('dragging');
}, 0);
});
card.addEventListener('dragend', () => {
card.classList.remove('dragging');
draggedItem = null;
});
});
kanbanColumns.forEach(column => {
column.addEventListener('dragover', (e) => {
e.preventDefault();
column.classList.add('highlight');
});
column.addEventListener('dragleave', () => {
column.classList.remove('highlight');
});
column.addEventListener('drop', (e) => {
e.preventDefault();
column.classList.remove('highlight');
if (draggedItem) {
const taskId = parseInt(draggedItem.dataset.id);
const newGroup = column.dataset.group;
const groupBy = kanbanGroupBy.value;
const task = tasks.find(t => t.id === taskId);
if (task) {
if (groupBy === 'status') {
task.status = newGroup;
} else {
task.category = newGroup;
}
renderKanbanView();
}
}
});
});
}
// Event listeners
listViewBtn.addEventListener('click', () => showView(listView));
kanbanViewBtn.addEventListener('click', () => showView(kanbanView));
cardViewBtn.addEventListener('click', () => showView(cardView));
addTaskBtn.addEventListener('click', () => {
modalTitle.textContent = 'Add New Task';
taskIdInput.value = '';
taskForm.reset();
showModal();
});
closeModal.addEventListener('click', hideModal);
cancelTask.addEventListener('click', hideModal);
taskForm.addEventListener('submit', (e) => {
e.preventDefault();
const taskData = {
id: taskIdInput.value ? parseInt(taskIdInput.value) : null,
description: taskDescription.value,
category: taskCategory.value,
status: taskStatus.value
};
if (taskData.id) {
updateTask(taskData);
} else {
addTask(taskData);
}
hideModal();
});
categoryFilter.addEventListener('change', renderCurrentView);
statusFilter.addEventListener('change', renderCurrentView);
kanbanGroupBy.addEventListener('change', renderKanbanView);
// Initialize
showView(listView);
</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=manyone/manyone-space" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
</html>