anycoder-9e962b94 / index.html
dshunt's picture
Upload folder using huggingface_hub
35b4be4 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TaskMaster Pro</title>
<!-- Import Google Fonts (Poppins & Inter) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Poppins:wght@600;700;800&display=swap" rel="stylesheet">
<!-- Import FontAwesome for Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
/* Modern Color Palette - Light Mode */
--primary-color: #6366f1;
--primary-hover: #4f46e5;
--secondary-color: #ec4899;
--bg-body: #f3f4f6;
--bg-card: #ffffff;
--text-main: #1f2937;
--text-secondary: #6b7280;
--border-color: #e5e7eb;
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Dark Mode Variables */
[data-theme="dark"] {
--bg-body: #0f172a;
--bg-card: #1e293b;
--text-main: #f1f5f9;
--text-secondary: #94a3b8;
--border-color: #334155;
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.5);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.5);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Inter', sans-serif;
background-color: var(--bg-body);
color: var(--text-main);
transition: var(--transition);
line-height: 1.6;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* --- Header Section --- */
header {
background-color: var(--bg-card);
padding: 1rem 2rem;
box-shadow: var(--shadow-sm);
display: flex;
justify-content: space-between;
align-items: center;
position: sticky;
top: 0;
z-index: 100;
border-bottom: 1px solid var(--border-color);
}
.brand {
display: flex;
align-items: center;
gap: 10px;
font-family: 'Poppins', sans-serif;
font-weight: 800;
font-size: 1.5rem;
color: var(--primary-color);
}
.brand i {
font-size: 1.8rem;
}
.header-controls {
display: flex;
align-items: center;
gap: 1.5rem;
}
.theme-toggle {
cursor: pointer;
font-size: 1.2rem;
color: var(--text-secondary);
transition: color 0.3s;
padding: 8px;
border-radius: 50%;
background: transparent;
border: none;
}
.theme-toggle:hover {
background-color: var(--bg-body);
color: var(--primary-color);
}
.user-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 1rem;
}
/* --- Main Layout --- */
main {
flex: 1;
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
width: 100%;
}
.dashboard-header {
margin-bottom: 2rem;
display: flex;
justify-content: space-between;
align-items: flex-end;
}
.dashboard-title h1 {
font-family: 'Poppins', sans-serif;
font-size: 2.5rem;
font-weight: 700;
}
.dashboard-title p {
color: var(--text-secondary);
margin-top: 0.5rem;
}
.stats-container {
display: flex;
gap: 1.5rem;
}
.stat-card {
background-color: var(--bg-card);
padding: 1rem 1.5rem;
border-radius: 12px;
box-shadow: var(--shadow-sm);
border-left: 4px solid var(--primary-color);
min-width: 150px;
}
.stat-value {
font-size: 2rem;
font-weight: 700;
color: var(--primary-color);
}
.stat-label {
color: var(--text-secondary);
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* --- Task Board (Kanban) --- */
.board-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
height: 100%;
}
.column {
background-color: var(--bg-body);
border-radius: 16px;
padding: 1.5rem;
display: flex;
flex-direction: column;
min-height: 600px;
transition: var(--transition);
}
.column-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 2px solid var(--border-color);
}
.column-title {
font-family: 'Poppins', sans-serif;
font-weight: 600;
font-size: 1.2rem;
display: flex;
align-items: center;
gap: 10px;
}
.task-count {
background-color: var(--bg-card);
padding: 2px 8px;
border-radius: 12px;
font-size: 0.8rem;
color: var(--text-secondary);
font-weight: 600;
}
.add-btn {
background: none;
border: none;
color: var(--primary-color);
cursor: pointer;
font-size: 1.2rem;
transition: transform 0.2s;
padding: 5px;
border-radius: 8px;
}
.add-btn:hover {
background-color: rgba(99, 102, 241, 0.1);
transform: scale(1.1);
}
.task-list {
flex: 1;
overflow-y: auto;
padding-right: 5px;
scrollbar-width: thin;
}
/* Custom Scrollbar */
.task-list::-webkit-scrollbar {
width: 6px;
}
.task-list::-webkit-scrollbar-track {
background: transparent;
}
.task-list::-webkit-scrollbar-thumb {
background-color: var(--border-color);
border-radius: 20px;
}
/* --- Task Cards --- */
.task-card {
background-color: var(--bg-card);
padding: 1.25rem;
border-radius: 12px;
margin-bottom: 1rem;
box-shadow: var(--shadow-sm);
transition: var(--transition);
border: 1px solid transparent;
cursor: grab;
position: relative;
}
.task-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
border-color: var(--primary-color);
}
.task-card.dragging {
opacity: 0.5;
cursor: grabbing;
}
.task-priority {
display: inline-block;
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
padding: 4px 8px;
border-radius: 6px;
margin-bottom: 0.75rem;
}
.priority-high { background-color: #fee2e2; color: #dc2626; }
.priority-medium { background-color: #fef3c7; color: #d97706; }
.priority-low { background-color: #dbeafe; color: #2563eb; }
.task-title {
font-family: 'Poppins', sans-serif;
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--text-main);
}
.task-desc {
font-size: 0.9rem;
color: var(--text-secondary);
margin-bottom: 1rem;
white-space: pre-wrap;
}
.task-meta {
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid var(--border-color);
padding-top: 0.75rem;
font-size: 0.85rem;
color: var(--text-secondary);
}
.due-date {
display: flex;
align-items: center;
gap: 5px;
}
.delete-btn {
background: none;
border: none;
color: #9ca3af;
cursor: pointer;
padding: 5px;
border-radius: 4px;
transition: all 0.2s;
}
.delete-btn:hover {
color: #ef4444;
background-color: #fee2e2;
}
/* --- Modal / Add Task Form --- */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
display: flex;
justify-content: center;
align-items: center;
z-index: 200;
opacity: 0;
visibility: hidden;
transition: var(--transition);
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
.modal {
background-color: var(--bg-card);
padding: 2rem;
border-radius: 16px;
width: 90%;
max-width: 500px;
box-shadow: var(--shadow-lg);
transform: scale(0.9);
transition: var(--transition);
}
.modal-overlay.active .modal {
transform: scale(1);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
}
.modal-title {
font-family: 'Poppins', sans-serif;
font-size: 1.5rem;
font-weight: 700;
}
.close-btn {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: var(--text-secondary);
}
.form-group {
margin-bottom: 1.25rem;
}
.form-label {
display: block;
margin-bottom: 0.5rem;
font-size: 0.9rem;
font-weight: 500;
color: var(--text-main);
}
.form-input {
width: 100%;
padding: 0.75rem;
border-radius: 8px;
border: 1px solid var(--border-color);
background-color: var(--bg-body);
color: var(--text-main);
font-family: 'Inter', sans-serif;
font-size: 1rem;
transition: var(--transition);
}
.form-input:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
}
textarea.form-input {
resize: vertical;
min-height: 100px;
}
.form-select {
width: 100%;
padding: 0.75rem;
border-radius: 8px;
border: 1px solid var(--border-color);
background-color: var(--bg-body);
color: var(--text-main);
font-family: 'Inter', sans-serif;
font-size: 1rem;
cursor: pointer;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 1rem;
margin-top: 2rem;
}
.btn {
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-weight: 600;
font-size: 0.95rem;
cursor: pointer;
transition: var(--transition);
border: none;
font-family: 'Inter', sans-serif;
}
.btn-primary {
background-color: var(--primary-color);
color: white;
}
.btn-primary:hover {
background-color: var(--primary-hover);
transform: translateY(-1px);
}
.btn-secondary {
background-color: transparent;
color: var(--text-secondary);
border: 1px solid var(--border-color);
}
.btn-secondary:hover {
background-color: var(--bg-body);
color: var(--text-main);
}
/* --- Footer --- */
footer {
text-align: center;
padding: 2rem;
color: var(--text-secondary);
font-size: 0.9rem;
border-top: 1px solid var(--border-color);
margin-top: auto;
}
.footer-link {
color: var(--primary-color);
text-decoration: none;
font-weight: 600;
}
.footer-link:hover {
text-decoration: underline;
}
/* --- Responsive Mobile --- */
@media (max-width: 768px) {
.dashboard-title h1 { font-size: 2rem; }
.stats-container { grid-template-columns: 1fr 1fr; }
.header-controls { gap: 1rem; }
.brand span { display: none; }
}
</style>
</head>
<body>
<!-- Header -->
<header>
<div class="brand">
<i class="fa-solid fa-rocket"></i>
<span>TaskMaster</span>
</div>
<div class="header-controls">
<button class="theme-toggle" id="themeToggle" title="Toggle Theme">
<i class="fa-solid fa-moon"></i>
</button>
<div class="user-avatar">JD</div>
</div>
</header>
<!-- Main Content -->
<main>
<div class="dashboard-header">
<div class="dashboard-title">
<h1>My Dashboard</h1>
<p>Welcome back, John! You have <span id="totalTasks" style="color:var(--primary-color); font-weight:700;">0</span> pending tasks.</p>
</div>
<div class="stats-container">
<div class="stat-card">
<div class="stat-value" id="pendingCount">0</div>
<div class="stat-label">To Do</div>
</div>
<div class="stat-card" style="border-color: #f59e0b;">
<div class="stat-value" id="progressCount">0</div>
<div class="stat-label">In Progress</div>
</div>
<div class="stat-card" style="border-color: #10b981;">
<div class="stat-value" id="doneCount">0</div>
<div class="stat-label">Completed</div>
</div>
</div>
</div>
<!-- Kanban Board -->
<div class="board-container">
<!-- Column: To Do -->
<div class="column" id="todo">
<div class="column-header">
<div class="column-title">
<i class="fa-solid fa-list-check" style="color: #6366f1;"></i>
To Do
<span class="task-count" id="count-todo">0</span>
</div>
<button class="add-btn" onclick="openModal('todo')">
<i class="fa-solid fa-plus"></i>
</button>
</div>
<div class="task-list" id="list-todo">
<!-- Tasks injected here -->
</div>
</div>
<!-- Column: In Progress -->
<div class="column" id="inprogress">
<div class="column-header">
<div class="column-title">
<i class="fa-solid fa-circle-play" style="color: #f59e0b;"></i>
In Progress
<span class="task-count" id="count-inprogress">0</span>
</div>
<button class="add-btn" onclick="openModal('inprogress')">
<i class="fa-solid fa-plus"></i>
</button>
</div>
<div class="task-list" id="list-inprogress">
<!-- Tasks injected here -->
</div>
</div>
<!-- Column: Done -->
<div class="column" id="done">
<div class="column-header">
<div class="column-title">
<i class="fa-solid fa-check-to-slot" style="color: #10b981;"></i>
Completed
<span class="task-count" id="count-done">0</span>
</div>
</div>
<div class="task-list" id="list-done">
<!-- Tasks injected here -->
</div>
</div>
</div>
</main>
<!-- Footer with Requirement -->
<footer>
<p>&copy; 2023 TaskMaster Pro. Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="footer-link">any coder</a>.</p>
</footer>
<!-- Add Task Modal -->
<div class="modal-overlay" id="taskModal">
<div class="modal">
<div class="modal-header">
<h2 class="modal-title" id="modalTitle">Add New Task</h2>
<button class="close-btn" onclick="closeModal()">&times;</button>
</div>
<form id="taskForm">
<input type="hidden" id="taskId">
<div class="form-group">
<label class="form-label" for="taskTitle">Task Title</label>
<input type="text" id="taskTitle" class="form-input" placeholder="e.g., Redesign Homepage" required>
</div>
<div class="form-group">
<label class="form-label" for="taskDesc">Description</label>
<textarea id="taskDesc" class="form-input" placeholder="Add details about this task..."></textarea>
</div>
<div class="form-group">
<label class="form-label" for="taskPriority">Priority</label>
<select id="taskPriority" class="form-select">
<option value="low">Low Priority</option>
<option value="medium" selected>Medium Priority</option>
<option value="high">High Priority</option>
</select>
</div>
<div class="form-group">
<label class="form-label" for="taskStatus">Move To</label>
<select id="taskStatus" class="form-select">
<option value="todo">To Do</option>
<option value="inprogress">In Progress</option>
<option value="done">Completed</option>
</select>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="closeModal()">Cancel</button>
<button type="submit" class="btn btn-primary">Save Task</button>
</div>
</form>
</div>
</div>
<script>
// --- State Management ---
let tasks = JSON.parse(localStorage.getItem('taskMasterData')) || [
{ id: 1, title: "Welcome to TaskMaster", desc: "This is a demo task. You can drag and drop this card!", priority: "low", status: "todo", date: new Date().toISOString().split('T')[0] },
{ id: 2, title: "Update Settings", desc: "Remember to change the theme color in settings.", priority: "medium", status: "inprogress", date: new Date().toISOString().split('T')[0] },
{ id: 3, title: "Complete Project", desc: "Finish the HTML, CSS, and JS parts.", priority: "high", status: "done", date: new Date().toISOString().split('T')[0] }
];
let draggedItem = null;
// --- DOM Elements ---
const themeToggle = document.getElementById('themeToggle');
const modal = document.getElementById('taskModal');
const taskForm = document.getElementById('taskForm');
const modalTitle = document.getElementById('modalTitle');
const taskLists = {
todo: document.getElementById('list-todo'),
inprogress: document.getElementById('list-inprogress'),
done: document.getElementById('list-done')
};
// --- Initialization ---
document.addEventListener('DOMContentLoaded', () => {
renderTasks();
updateStats();
checkTheme();
});
// --- Theme Logic ---
function checkTheme() {
const savedTheme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', savedTheme);
updateThemeIcon(savedTheme);
}
themeToggle.addEventListener('click', () => {
const currentTheme = document.documentElement.getAttribute('data-theme');
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
updateThemeIcon(newTheme);
});
function updateThemeIcon(theme) {
const icon = themeToggle.querySelector('i');
if (theme === 'dark') {
icon.className = 'fa-solid fa-sun';
} else {
icon.className = 'fa-solid fa-moon';
}
}
// --- Task Rendering ---
function renderTasks() {
// Clear lists
taskLists.todo.innerHTML = '';
taskLists.inprogress.innerHTML = '';
taskLists.done.innerHTML = '';
tasks.forEach(task => {
const card = createTaskCard(task);
taskLists[task.status].appendChild(card);
});
updateStats();
saveData();
}
function createTaskCard(task) {
const card = document.createElement('div');
card.className = 'task-card';
card.draggable = true;
card.id = task.id;
// Priority Styles
const priorityClass = `priority-${task.priority}`;
const priorityLabel = task.priority.charAt(0).toUpperCase() + task.priority.slice(1);
card.innerHTML = `
<span class="task-priority ${priorityClass}">${priorityLabel}</span>
<div class="task-title">${escapeHtml(task.title)}</div>
<div class="task-desc">${escapeHtml(task.desc)}</div>
<div class="task-meta">
<span class="due-date">
<i class="fa-regular fa-calendar"></i> ${task.date}
</span>
<div style="display:flex; gap:5px;">
<button class="delete-btn" onclick="deleteTask(${task.id})" title="Delete">
<i class="fa-solid fa-trash"></i>
</button>
</div>
</div>
`;
// Drag Events
card.addEventListener('dragstart', handleDragStart);
card.addEventListener('dragend', handleDragEnd);
card.addEventListener('dragover', handleDragOver);
card.addEventListener('drop', handleDrop);
card.addEventListener('dragenter', handleDragEnter);
card.addEventListener('dragleave', handleDragLeave);
return card;
}
function updateStats() {
const total = tasks.length;
const pending = tasks.filter(t => t.status === 'todo').length;
const progress = tasks.filter(t => t.status === 'inprogress').length;
const done = tasks.filter(t => t.status === 'done').length;
document.getElementById('totalTasks').innerText = total;
document.getElementById('pendingCount').innerText = pending;
document.getElementById('progressCount').innerText = progress;
document.getElementById('doneCount').innerText = done;
document.getElementById('count-todo').innerText = pending;
document.getElementById('count-inprogress').innerText = progress;
document.getElementById('count-done').innerText = done;
}
// --- Drag & Drop Logic ---
function handleDragStart(e) {
draggedItem = this;
setTimeout(() => this.classList.add('dragging'), 0);
}
function handleDragEnd(e) {
this.classList.remove('dragging');
draggedItem = null;
}
function handleDragOver(e) {
e.preventDefault(); // Necessary to allow dropping
}
function handleDragEnter(e) {
e.preventDefault();
if (this !== draggedItem && this.classList.contains('task-list')) {
this.style.borderColor = 'var(--primary-color)';
this.style.backgroundColor = 'var(--bg-card)';
}
}
function handleDragLeave(e) {
if (this.classList.contains('task-list')) {
this.style.borderColor = 'transparent';
this.style.backgroundColor = 'var(--bg-body)';
}
}
function handleDrop(e) {
e.preventDefault();
this.style.borderColor = 'transparent';
this.style.backgroundColor = 'var(--bg-body)';
if (draggedItem && draggedItem !== this) {
// Find the target task ID (if dropping on a card) or just append (if dropping on empty list)
let targetTask = this;
// If we dropped on the task-list container directly
if (this.classList.contains('task-list')) {
this.appendChild(draggedItem);
updateTaskStatus(draggedItem.id, this.id.replace('list-', ''));
return;
}
// If we dropped on another card
if (this.classList.contains('task-card')) {
this.after(draggedItem);
updateTaskStatus(draggedItem.id, this.parentElement.id.replace('list-', ''));
}
}
}
function updateTaskStatus(taskId, newStatus) {
const taskIndex = tasks.findIndex(t => t.id === taskId);
if (taskIndex > -1) {
tasks[taskIndex].status = newStatus;
saveData();
updateStats();
}
}
// --- Modal & Form Logic ---
function openModal(status = 'todo') {
modal.classList.add('active');
document.getElementById('modalTitle').innerText = "Add New Task";
taskForm.reset();
document.getElementById('taskStatus').value = status;
document.getElementById('taskId').value = '';
}
function closeModal() {
modal.classList.remove('active');
taskForm.reset();
}
taskForm.addEventListener('submit', (e) => {
e.preventDefault();
const id = document.getElementById('taskId').value;
const title = document.getElementById('taskTitle').value;
const desc = document.getElementById('taskDesc').value;
const priority = document.getElementById('taskPriority').value;
const status = document.getElementById('taskStatus').value;
const date = new Date().toISOString().split('T')[0];
if (id) {
// Edit existing
const taskIndex = tasks.findIndex(t => t.id == id);
if (taskIndex > -1) {
tasks[taskIndex] = { ...tasks[taskIndex], title, desc, priority, status, date };
}
} else {
// Create new
const newTask = {
id: Date.now(),
title,
desc,
priority,
status,
date
};
tasks.push(newTask);
}
saveData();
renderTasks();
closeModal();
});
function deleteTask(id) {
if (confirm('Are you sure you want to delete this task?')) {
tasks = tasks.filter(task => task.id !== id);
saveData();
renderTasks();
}
}
// --- Utilities ---
function saveData() {
localStorage.setItem('taskMasterData', JSON.stringify(tasks));
}
function escapeHtml(text) {
if (!text) return "";
return text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
// Close modal if clicking outside
modal.addEventListener('click', (e) => {
if (e.target === modal) closeModal();
});
</script>
</body>
</html>