anycoder-cf878584 / index.html
Tahersaeedi's picture
Upload folder using huggingface_hub
1806a1e verified
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>مدیریت وظایف و تمرکز | Task & Focus Manager</title>
<!-- Font Awesome for Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Google Fonts (Vazirmatn for Persian) -->
<link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;700&display=swap" rel="stylesheet">
<style>
:root {
--primary-color: #6366f1;
--primary-hover: #4f46e5;
--secondary-color: #ec4899;
--background-color: #f3f4f6;
--card-bg: #ffffff;
--text-primary: #1f2937;
--text-secondary: #6b7280;
--success: #10b981;
--danger: #ef4444;
--warning: #f59e0b;
--radius: 12px;
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--transition: all 0.3s ease;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
outline: none;
}
body {
font-family: 'Vazirmatn', sans-serif;
background-color: var(--background-color);
color: var(--text-primary);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
/* Header Styles */
header {
width: 100%;
max-width: 800px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding: 10px 0;
}
.brand {
font-size: 1.5rem;
font-weight: 700;
color: var(--primary-color);
display: flex;
align-items: center;
gap: 10px;
}
.anycoder-link {
font-size: 0.9rem;
color: var(--text-secondary);
text-decoration: none;
background: #e0e7ff;
color: var(--primary-color);
padding: 6px 12px;
border-radius: 20px;
transition: var(--transition);
font-weight: 500;
}
.anycoder-link:hover {
background: var(--primary-color);
color: white;
transform: translateY(-2px);
}
/* Main Container */
.container {
display: grid;
grid-template-columns: 1fr;
gap: 20px;
width: 100%;
max-width: 800px;
}
@media (min-width: 768px) {
.container {
grid-template-columns: 1.5fr 1fr;
}
}
/* Cards */
.card {
background: var(--card-bg);
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 20px;
overflow: hidden;
}
h2 {
font-size: 1.2rem;
margin-bottom: 15px;
border-bottom: 2px solid #f3f4f6;
padding-bottom: 10px;
color: var(--text-primary);
}
/* Input Group */
.input-group {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
input[type="text"] {
flex: 1;
padding: 12px;
border: 2px solid #e5e7eb;
border-radius: var(--radius);
font-family: inherit;
transition: var(--transition);
}
input[type="text"]:focus {
border-color: var(--primary-color);
}
button {
cursor: pointer;
border: none;
border-radius: var(--radius);
padding: 10px 20px;
font-family: inherit;
font-weight: 500;
transition: var(--transition);
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-primary:hover {
background: var(--primary-hover);
transform: translateY(-1px);
}
/* Task List */
.task-list {
list-style: none;
max-height: 400px;
overflow-y: auto;
}
.task-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
margin-bottom: 10px;
background: #f9fafb;
border-radius: 8px;
border-right: 4px solid transparent;
transition: var(--transition);
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.task-item:hover {
background: #f3f4f6;
transform: translateX(-5px);
}
.task-item.completed {
border-right-color: var(--success);
background: #f0fdf4;
}
.task-item.completed span {
text-decoration: line-through;
color: var(--text-secondary);
}
.task-actions {
display: flex;
gap: 8px;
}
.btn-icon {
padding: 6px;
border-radius: 6px;
color: white;
font-size: 0.8rem;
}
.btn-check { background: var(--success); }
.btn-delete { background: var(--danger); }
.btn-icon:hover { opacity: 0.9; }
.empty-state {
text-align: center;
color: var(--text-secondary);
padding: 20px;
font-size: 0.9rem;
}
/* Pomodoro Timer */
.timer-display {
font-size: 4rem;
font-weight: 700;
text-align: center;
color: var(--primary-color);
margin: 20px 0;
font-variant-numeric: tabular-nums;
letter-spacing: 2px;
}
.timer-controls {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.timer-modes {
display: flex;
justify-content: center;
gap: 10px;
margin-bottom: 20px;
}
.mode-btn {
background: transparent;
color: var(--text-secondary);
border: 1px solid #e5e7eb;
padding: 5px 10px;
font-size: 0.8rem;
}
.mode-btn.active {
background: var(--secondary-color);
color: white;
border-color: var(--secondary-color);
}
/* Stats */
.stats {
display: flex;
justify-content: space-around;
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #f3f4f6;
}
.stat-item {
text-align: center;
}
.stat-value {
font-size: 1.2rem;
font-weight: 700;
color: var(--text-primary);
}
.stat-label {
font-size: 0.8rem;
color: var(--text-secondary);
}
/* Toast Notification */
.toast-container {
position: fixed;
bottom: 20px;
left: 20px;
z-index: 1000;
}
.toast {
background: var(--text-primary);
color: white;
padding: 12px 20px;
border-radius: var(--radius);
margin-top: 10px;
box-shadow: var(--shadow);
display: flex;
align-items: center;
gap: 10px;
animation: slideUp 0.3s ease;
min-width: 250px;
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: #c7c7c7;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
</style>
</head>
<body>
<header>
<div class="brand">
<i class="fa-solid fa-layer-group"></i>
<span>تسک مستر</span>
</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" class="anycoder-link" target="_blank">Built with anycoder</a>
</header>
<div class="container">
<!-- Task Section -->
<div class="card">
<h2><i class="fa-solid fa-list-check"></i> لیست وظایف</h2>
<div class="input-group">
<input type="text" id="taskInput" placeholder="وظیفه جدید را وارد کنید..." onkeypress="handleKeyPress(event)">
<button class="btn-primary" onclick="addTask()">
<i class="fa-solid fa-plus"></i> افزودن
</button>
</div>
<ul class="task-list" id="taskList">
<!-- Tasks will be injected here -->
<li class="empty-state">هیچ وظیفه‌ای ثبت نشده است. روز خوبی داشته باشید!</li>
</ul>
<div class="stats">
<div class="stat-item">
<div class="stat-value" id="totalTasks">0</div>
<div class="stat-label">کل وظایف</div>
</div>
<div class="stat-item">
<div class="stat-value" id="completedTasks">0</div>
<div class="stat-label">انجام شده</div>
</div>
</div>
</div>
<!-- Timer Section -->
<div class="card">
<h2><i class="fa-solid fa-hourglass-start"></i> تایمر تمرکز</h2>
<div class="timer-modes">
<button class="mode-btn active" onclick="setTimerMode(25, this)">کار (25)</button>
<button class="mode-btn" onclick="setTimerMode(5, this)">استراحت کوتاه (5)</button>
<button class="mode-btn" onclick="setTimerMode(15, this)">استراحت بلند (15)</button>
</div>
<div class="timer-display" id="timer">25:00</div>
<div class="timer-controls">
<button class="btn-primary" id="startBtn" onclick="toggleTimer()" style="background: var(--success);">
<i class="fa-solid fa-play"></i> شروع
</button>
<button class="btn-primary" onclick="resetTimer()" style="background: var(--text-secondary);">
<i class="fa-solid fa-rotate-right"></i> بازنشانی
</button>
</div>
</div>
</div>
<div class="toast-container" id="toastContainer"></div>
<script>
// --- State Management ---
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
let timerInterval;
let timeLeft = 25 * 60;
let isTimerRunning = false;
let currentModeTime = 25 * 60;
// --- DOM Elements ---
const taskInput = document.getElementById('taskInput');
const taskList = document.getElementById('taskList');
const totalTasksEl = document.getElementById('totalTasks');
const completedTasksEl = document.getElementById('completedTasks');
const timerDisplay = document.getElementById('timer');
const startBtn = document.getElementById('startBtn');
const toastContainer = document.getElementById('toastContainer');
// --- Initialization ---
document.addEventListener('DOMContentLoaded', () => {
renderTasks();
updateStats();
});
// --- Task Functions ---
function addTask() {
const text = taskInput.value.trim();
if (text === '') {
showToast('لطفا متنی برای وظیفه وارد کنید', 'warning');
return;
}
const newTask = {
id: Date.now(),
text: text,
completed: false
};
tasks.unshift(newTask); // Add to top
saveTasks();
renderTasks();
updateStats();
taskInput.value = '';
showToast('وظیفه جدید اضافه شد', 'success');
}
function handleKeyPress(event) {
if (event.key === 'Enter') {
addTask();
}
}
function toggleTask(id) {
tasks = tasks.map(task =>
task.id === id ? { ...task, completed: !task.completed } : task
);
saveTasks();
renderTasks();
updateStats();
}
function deleteTask(id) {
// Add a small confirmation delay or UI effect could be nice, but keeping it snappy
tasks = tasks.filter(task => task.id !== id);
saveTasks();
renderTasks();
updateStats();
showToast('وظیفه حذف شد', 'info');
}
function saveTasks() {
localStorage.setItem('tasks', JSON.stringify(tasks));
}
function renderTasks() {
taskList.innerHTML = '';
if (tasks.length === 0) {
taskList.innerHTML = '<li class="empty-state">هیچ وظیفه‌ای ثبت نشده است. روز خوبی داشته باشید!</li>';
return;
}
tasks.forEach(task => {
const li = document.createElement('li');
li.className = `task-item ${task.completed ? 'completed' : ''}`;
li.innerHTML = `
<span>${escapeHtml(task.text)}</span>
<div class="task-actions">
<button class="btn-icon btn-check" onclick="toggleTask(${task.id})" title="${task.completed ? 'بازگردانی' : 'انجام شد'}">
<i class="fa-solid ${task.completed ? 'fa-rotate-left' : 'fa-check'}"></i>
</button>
<button class="btn-icon btn-delete" onclick="deleteTask(${task.id})" title="حذف">
<i class="fa-solid fa-trash"></i>
</button>
</div>
`;
taskList.appendChild(li);
});
}
function updateStats() {
const total = tasks.length;
const completed = tasks.filter(t => t.completed).length;
totalTasksEl.textContent = total;
completedTasksEl.textContent = completed;
}
// Security: Prevent XSS
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// --- Timer Functions ---
function formatTime(seconds) {
const m = Math.floor(seconds / 60);
const s = seconds % 60;
return `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
}
function updateTimerDisplay() {
timerDisplay.textContent = formatTime(timeLeft);
document.title = `${formatTime(timeLeft)} - مدیریت وظایف`;
}
function toggleTimer() {
if (isTimerRunning) {
clearInterval(timerInterval);
isTimerRunning = false;
startBtn.innerHTML = '<i class="fa-solid fa-play"></i> ادامه';
startBtn.style.background = 'var(--success)';
} else {
if (timeLeft <= 0) timeLeft = currentModeTime;
isTimerRunning = true;
startBtn.innerHTML = '<i class="fa-solid fa-pause"></i> توقف';
startBtn.style.background = 'var(--warning)';
timerInterval = setInterval(() => {
timeLeft--;
updateTimerDisplay();
if (timeLeft <= 0) {
clearInterval(timerInterval);
isTimerRunning = false;
startBtn.innerHTML = '<i class="fa-solid fa-play"></i> شروع';
startBtn.style.background = 'var(--success)';
playAlarm();
showToast('زمان به پایان رسید!', 'success');
document.title = 'زمان تمام شد!';
}
}, 1000);
}
}
function resetTimer() {
clearInterval(timerInterval);
isTimerRunning = false;
timeLeft = currentModeTime;
updateTimerDisplay();
startBtn.innerHTML = '<i class="fa-solid fa-play"></i> شروع';
startBtn.style.background = 'var(--success)';
document.title = 'مدیریت وظایف و تمرکز';
}
function setTimerMode(minutes, btnElement) {
// Update UI active state
document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active'));
btnElement.classList.add('active');
// Reset Logic
clearInterval(timerInterval);
isTimerRunning = false;
startBtn.innerHTML = '<i class="fa-solid fa-play"></i> شروع';
startBtn.style.background = 'var(--success)';
currentModeTime = minutes * 60;
timeLeft = currentModeTime;
updateTimerDisplay();
}
function playAlarm() {
// Simple beep using AudioContext for modern browsers without external assets
try {
const AudioContext = window.AudioContext || window.webkitAudioContext;
if (AudioContext) {
const ctx = new AudioContext();
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(ctx.destination);
osc.type = 'sine';
osc.frequency.value = 880; // A5
gain.gain.value = 0.1;
osc.start();
setTimeout(() => {
osc.stop();
// Second beep
setTimeout(() => {
const osc2 = ctx.createOscillator();
const gain2 = ctx.createGain();
osc2.connect(gain2);
gain2.connect(ctx.destination);
osc2.type = 'sine';
osc2.frequency.value = 880;
gain2.gain.value = 0.1;
osc2.start();
setTimeout(() => osc2.stop(), 300);
}, 200);
}, 300);
}
} catch (e) {
console.log("Audio not supported");
}
}
// --- Toast Notification System ---
function showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.className = 'toast';
let icon = 'fa-info-circle';
let color = 'var(--primary-color)';
if (type === 'success') { icon = 'fa-check-circle'; color = 'var(--success)'; }
if (type === 'warning') { icon = 'fa-exclamation-triangle'; color = 'var(--warning)'; }
if (type === 'error') { icon = 'fa-times-circle'; color = 'var(--danger)'; }
toast.style.borderRight = `4px solid ${color}`;
toast.innerHTML = `
<i class="fa-solid ${icon}" style="color: ${color}"></i>
<span>${message}</span>
`;
toastContainer.appendChild(toast);
// Remove after 3 seconds
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translateY(20px)';
setTimeout(() => {
toast.remove();
}, 300);
}, 3000);
}
</script>
</body>
</html>