Write a code offline la
Write a code for offline app for app which calculate all daily expenses it will write and also make to do list
mkdir -p /home/daytona/workspace/daily-app && ls /home/daytona/workspace/daily-app
~/daily-app/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Daily Planner - Expenses & To-Do</title>
<style>
/* ===== RESET & BASE ===== */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #0f172a;
--surface: #1e293b;
--surface2: #273449;
--border: #334155;
--accent: #6366f1;
--accent-hover: #4f46e5;
--green: #22c55e;
--red: #ef4444;
--yellow: #f59e0b;
--text: #f1f5f9;
--text2: #94a3b8;
--radius: 12px;
--shadow: 0 4px 24px rgba(0,0,0,0.4);
}
body {
font-family: 'Segoe UI', system-ui, sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100vh;
padding: 20px 16px 60px;
}
h1.app-title {
text-align: center;
font-size: 1.6rem;
font-weight: 700;
margin-bottom: 6px;
background: linear-gradient(135deg, var(--accent), #a78bfa);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.app-subtitle {
text-align: center;
color: var(--text2);
font-size: 0.85rem;
margin-bottom: 28px;
}
/* ===== TABS ===== */
.tabs {
display: flex;
gap: 8px;
justify-content: center;
margin-bottom: 24px;
}
.tab-btn {
padding: 9px 28px;
border-radius: 40px;
border: 2px solid var(--border);
background: var(--surface);
color: var(--text2);
font-size: 0.92rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.tab-btn.active {
background: var(--accent);
border-color: var(--accent);
color: #fff;
}
.tab-btn:hover:not(.active) { border-color: var(--accent); color: var(--text); }
/* ===== PANELS ===== */
.panel { display: none; max-width: 680px; margin: 0 auto; }
.panel.active { display: block; }
/* ===== CARD ===== */
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 20px;
margin-bottom: 18px;
box-shadow: var(--shadow);
}
.card h2 {
font-size: 1rem;
font-weight: 600;
color: var(--text2);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 14px;
}
/* ===== INPUTS ===== */
.input-row {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
input[type="text"], input[type="number"], input[type="date"], select {
flex: 1 1 120px;
padding: 10px 14px;
border-radius: 8px;
border: 1.5px solid var(--border);
background: var(--surface2);
color: var(--text);
font-size: 0.93rem;
outline: none;
transition: border 0.2s;
}
input:focus, select:focus { border-color: var(--accent); }
input::placeholder { color: var(--text2); }
.btn {
padding: 10px 20px;
border-radius: 8px;
border: none;
font-size: 0.92rem;
font-weight: 600;
cursor: pointer;
transition: background 0.2s, transform 0.1s;
}
.btn:active { transform: scale(0.97); }
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-hover); }
.btn-danger { background: transparent; color: var(--red); border: 1.5px solid var(--red); padding: 5px 10px; font-size: 0.8rem; border-radius: 6px; }
.btn-danger:hover { background: var(--red); color: #fff; }
.btn-sm-green { background: transparent; color: var(--green); border: 1.5px solid var(--green); padding: 5px 10px; font-size: 0.8rem; border-radius: 6px; }
.btn-sm-green:hover { background: var(--green); color: #fff; }
/* ===== DATE FILTER ===== */
.date-filter {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
margin-bottom: 4px;
}
.date-filter label { color: var(--text2); font-size: 0.85rem; }
/* ===== SUMMARY STRIP ===== */
.summary-strip {
display: flex;
gap: 12px;
flex-wrap: wrap;
margin-bottom: 18px;
}
.summary-box {
flex: 1 1 140px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 16px;
text-align: center;
}
.summary-box .label { font-size: 0.75rem; color: var(--text2); text-transform: uppercase; letter-spacing: 0.05em; }
.summary-box .value { font-size: 1.5rem; font-weight: 700; margin-top: 4px; }
.value.total { color: var(--accent); }
.value.today { color: var(--yellow); }
.value.count { color: var(--green); }
/* ===== EXPENSE LIST ===== */
.expense-list { list-style: none; }
.expense-list li {
display: flex;
align-items: center;
gap: 10px;
padding: 11px 0;
border-bottom: 1px solid var(--border);
font-size: 0.9rem;
}
.expense-list li:last-child { border-bottom: none; }
.exp-icon {
width: 34px; height: 34px;
border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 1.1rem;
flex-shrink: 0;
}
.exp-info { flex: 1; }
.exp-name { font-weight: 600; }
.exp-meta { font-size: 0.78rem; color: var(--text2); margin-top: 2px; }
.exp-amount { font-weight: 700; font-size: 1rem; white-space: nowrap; }
.exp-amount.neg { color: var(--red); }
.exp-amount.pos { color: var(--green); }
.empty-msg {
text-align: center;
color: var(--text2);
padding: 28px 0;
font-size: 0.9rem;
}
/* ===== CATEGORY BADGE ===== */
.badge {
display: inline-block;
padding: 2px 8px;
border-radius: 20px;
font-size: 0.72rem;
font-weight: 600;
}
/* ===== TODO LIST ===== */
.todo-list { list-style: none; }
.todo-item {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 12px 0;
border-bottom: 1px solid var(--border);
}
.todo-item:last-child { border-bottom: none; }
.todo-check {
width: 20px; height: 20px;
border-radius: 50%;
border: 2px solid var(--border);
flex-shrink: 0;
cursor: pointer;
margin-top: 2px;
display: flex; align-items: center; justify-content: center;
transition: all 0.2s;
}
.todo-check.done { background: var(--green); border-color: var(--green); }
.todo-check.done::after { content: 'โ'; color: #fff; font-size: 0.7rem; font-weight: 700; }
.todo-text-wrap { flex: 1; }
.todo-text { font-size: 0.93rem; transition: all 0.2s; }
.todo-text.done { text-decoration: line-through; color: var(--text2); }
.todo-priority { font-size: 0.72rem; margin-top: 3px; }
.todo-date-label { font-size: 0.72rem; color: var(--text2); margin-top: 2px; }
.todo-actions { display: flex; gap: 6px; }
/* Priority colors */
.p-high { color: var(--red); }
.p-medium { color: var(--yellow); }
.p-low { color: var(--green); }
/* ===== FILTER ROW ===== */
.filter-row {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 4px;
}
.filter-btn {
padding: 5px 14px;
border-radius: 20px;
border: 1.5px solid var(--border);
background: transparent;
color: var(--text2);
font-size: 0.8rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.filter-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; }
/* ===== PROGRESS BAR ===== */
.progress-bar-wrap { margin-top: 8px; }
.progress-label { display: flex; justify-content: space-between; font-size: 0.78rem; color: var(--text2); margin-bottom: 4px; }
.progress-bar { height: 6px; background: var(--border); border-radius: 4px; overflow: hidden; }
.progress-fill { height: 100%; background: var(--accent); border-radius: 4px; transition: width 0.4s; }
/* ===== CHART AREA ===== */
.chart-wrap { margin-top: 4px; }
.cat-row {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 10px;
}
.cat-label { width: 80px; font-size: 0.8rem; color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cat-bar-wrap { flex: 1; height: 10px; background: var(--border); border-radius: 5px; overflow: hidden; }
.cat-bar { height: 100%; border-radius: 5px; transition: width 0.4s; }
.cat-val { font-size: 0.8rem; font-weight: 600; width: 60px; text-align: right; }
/* ===== CLEAR ALL BUTTON ===== */
.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; }
.section-header h2 { margin-bottom: 0; }
/* ===== TOAST ===== */
#toast {
position: fixed; bottom: 28px; left: 50%; transform: translateX(-50%) translateY(80px);
background: var(--surface);
border: 1px solid var(--border);
border-radius: 40px;
padding: 10px 24px;
font-size: 0.88rem;
color: var(--text);
box-shadow: var(--shadow);
transition: transform 0.3s ease;
z-index: 999;
white-space: nowrap;
}
#toast.show { transform: translateX(-50%) translateY(0); }
/* ===== SCROLLABLE LIST ===== */
.scroll-list { max-height: 340px; overflow-y: auto; padding-right: 4px; }
.scroll-list::-webkit-scrollbar { width: 4px; }
.scroll-list::-webkit-scrollbar-track { background: transparent; }
.scroll-list::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
</style>
</head>
<body>
<h1 class="app-title">๐ Daily Planner</h1>
<p class="app-subtitle">Offline - All data saved in your browser</p>
<!-- TABS -->
<div class="tabs">
<button class="tab-btn active" onclick="switchTab('expenses')">๐ธ Expenses</button>
<button class="tab-btn" onclick="switchTab('todo')">โ
To-Do List</button>
</div>
<!-- ==================== EXPENSES PANEL ==================== -->
<div class="panel active" id="panel-expenses">
<!-- Summary -->
<div class="summary-strip">
<div class="summary-box">
<div class="label">Today</div>
<div class="value today" id="sum-today">โน0</div>
</div>
<div class="summary-box">
<div class="label">This Month</div>
<div class="value total" id="sum-month">โน0</div>
</div>
<div class="summary-box">
<div class="label">All Time</div>
<div class="value count" id="sum-all">โน0</div>
</div>
</div>
<!-- Add Expense -->
<div class="card">
<h2>Add Expense</h2>
<div class="input-row" style="margin-bottom:10px">
<input type="text" id="exp-name" placeholder="Description (e.g. Lunch)" />
<input type="number" id="exp-amount" placeholder="Amount (โน)" min="0" step="0.01" />
</div>
<div class="input-row">
<select id="exp-category">
<option value="food">๐ Food</option>
<option value="transport">๐ Transport</option>
<option value="shopping">๐ Shopping</option>
<option value="health">๐ Health</option>
<option value="bills">๐ก Bills</option>
<option value="entertainment">๐ฌ Entertainment</option>
<option value="education">๐ Education</option>
<option value="other">๐ฆ Other</option>
</select>
<input type="date" id="exp-date" />
<button class="btn btn-primary" onclick="addExpense()">+ Add</button>
</div>
</div>
<!-- Category Chart -->
<div class="card" id="chart-card">
<h2>Spending by Category</h2>
<div class="chart-wrap" id="cat-chart"></div>
</div>
<!-- Expense List -->
<div class="card">
<div class="section-header">
<h2>Expense Log</h2>
<button class="btn btn-danger" onclick="clearAllExpenses()">Clear All</button>
</div>
<div class="date-filter">
<label>Filter:</label>
<input type="date" id="filter-from" onchange="renderExpenses()" />
<span style="color:var(--text2)">to</span>
<input type="date" id="filter-to" onchange="renderExpenses()" />
<button class="btn" style="padding:6px 12px;font-size:0.8rem;background:var(--surface2);color:var(--text2)" onclick="clearFilter()">Reset</button>
</div>
<div class="scroll-list">
<ul class="expense-list" id="expense-list"></ul>
</div>
</div>
</div>
<!-- ==================== TODO PANEL ==================== -->
<div class="panel" id="panel-todo">
<!-- Add Todo -->
<div class="card">
<h2>Add Task</h2>
<div class="input-row" style="margin-bottom:10px">
<input type="text" id="todo-text" placeholder="Task description..." />
</div>
<div class="input-row">
<select id="todo-priority">
<option value="high">๐ด High Priority</option>
<option value="medium" selected>๐ก Medium Priority</option>
<option value="low">๐ข Low Priority</option>
</select>
<input type="date" id="todo-due" title="Due date (optional)" />
<button class="btn btn-primary" onclick="addTodo()">+ Add Task</button>
</div>
</div>
<!-- Progress -->
<div class="card">
<h2>Progress</h2>
<div id="todo-progress-text" style="font-size:0.9rem;color:var(--text2)">0 of 0 tasks completed</div>
<div class="progress-bar-wrap">
<div class="progress-bar"><div class="progress-fill" id="todo-progress-fill" style="width:0%"></div></div>
</div>
</div>
<!-- Todo List -->
<div class="card">
<div class="section-header">
<h2>Tasks</h2>
<button class="btn btn-danger" onclick="clearDoneTodos()">Clear Done</button>
</div>
<div class="filter-row">
<button class="filter-btn active" onclick="setTodoFilter('all', this)">All</button>
<button class="filter-btn" onclick="setTodoFilter('active', this)">Active</button>
<button class="filter-btn" onclick="setTodoFilter('done', this)">Done</button>
<button class="filter-btn" onclick="setTodoFilter('high', this)">๐ด High</button>
</div>
<div class="scroll-list" style="margin-top:8px">
<ul class="todo-list" id="todo-list"></ul>
</div>
</div>
</div>
<!-- Toast -->
<div id="toast"></div>
<script>
// ============================================================
// UTILITIES
// ============================================================
const $ = id => document.getElementById(id);
function toast(msg, color = '#6366f1') {
const t = $('toast');
t.textContent = msg;
t.style.borderColor = color;
t.classList.add('show');
setTimeout(() => t.classList.remove('show'), 2600);
}
function fmtMoney(n) {
return 'โน' + Number(n).toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
function todayStr() {
return new Date().toISOString().split('T')[0];
}
function formatDate(str) {
if (!str) return '';
const d = new Date(str + 'T00:00:00');
return d.toLocaleDateString('en-IN', { day: 'numeric', month: 'short', year: 'numeric' });
}
// ============================================================
// TABS
// ============================================================
function switchTab(tab) {
document.querySelectorAll('.tab-btn').forEach((b, i) => b.classList.toggle('active', i === (tab === 'expenses' ? 0 : 1)));
$('panel-expenses').classList.toggle('active', tab === 'expenses');
$('panel-todo').classList.toggle('active', tab === 'todo');
}
// ============================================================
// CATEGORY CONFIG
// ============================================================
const CATS = {
food: { icon: '๐', color: '#f59e0b', bg: '#78350f22' },
transport: { icon: '๐', color: '#3b82f6', bg: '#1e3a8a22' },
shopping: { icon: '๐', color: '#a855f7', bg: '#4c1d9522' },
health: { icon: '๐', color: '#22c55e', bg: '#14532d22' },
bills: { icon: '๐ก', color: '#ef4444', bg: '#7f1d1d22' },
entertainment: { icon: '๐ฌ', color: '#ec4899', bg: '#83185322' },
education: { icon: '๐', color: '#06b6d4', bg: '#16424422' },
other: { icon: '๐ฆ', color: '#94a3b8', bg: '#33415522' },
};
// ============================================================
// EXPENSES
// ============================================================
let expenses = JSON.parse(localStorage.getItem('dp_expenses') || '[]');
function saveExpenses() { localStorage.setItem('dp_expenses', JSON.stringify(expenses)); }
function addExpense() {
const name = $('exp-name').value.trim();
const amount = parseFloat($('exp-amount').value);
const cat = $('exp-category').value;
const date = $('exp-date').value || todayStr();
if (!name) return toast('Enter a description', '#ef4444');
if (!amount || amount <= 0) return toast('Enter a valid amount', '#ef4444');
expenses.unshift({ id: Date.now(), name, amount, cat, date });
saveExpenses();
$('exp-name').value = '';
$('exp-amount').value = '';
renderExpenses();
toast('Expense added!', '#22c55e');
}
function deleteExpense(id) {
expenses = expenses.filter(e => e.id !== id);
saveExpenses();
renderExpenses();
toast('Deleted', '#ef4444');
}
function clearAllExpenses() {
if (!confirm('Delete ALL expenses?')) return;
expenses = [];
saveExpenses();
renderExpenses();
toast('All expenses cleared');
}
function clearFilter() {
$('filter-from').value = '';
$('filter-to').value = '';
renderExpenses();
}
function renderExpenses() {
const from = $('filter-from').value;
const to = $('filter-to').value;
const filtered = expenses.filter(e => {
if (from && e.date < from) return false;
if (to && e.date > to) return false;
return true;
});
// Summary
const today = todayStr();
const thisMonth = today.slice(0, 7);
const todaySum = expenses.filter(e => e.date === today).reduce((s, e) => s + e.amount, 0);
const monthSum = expenses.filter(e => e.date.startsWith(thisMonth)).reduce((s, e) => s + e.amount, 0);
const allSum = expenses.reduce((s, e) => s + e.amount, 0);
$('sum-today').textContent = fmtMoney(todaySum);
$('sum-month').textContent = fmtMoney(monthSum);
$('sum-all').textContent = fmtMoney(allSum);
// List
const ul = $('expense-list');
if (filtered.length === 0) {
ul.innerHTML = '<li><div class="empty-msg">No expenses found.</div></li>';
} else {
ul.innerHTML = filtered.map(e => {
const c = CATS[e.cat] || CATS.other;
return `
<li>
<div class="exp-icon" style="background:${c.bg};">${c.icon}</div>
<div class="exp-info">
<div class="exp-name">${escHtml(e.name)}</div>
<div class="exp-meta">
<span class="badge" style="background:${c.bg};color:${c.color}">${e.cat}</span>
${formatDate(e.date)}
</div>
</div>
<div class="exp-amount neg">${fmtMoney(e.amount)}</div>
<button class="btn btn-danger" onclick="deleteExpense(${e.id})">โ</button>
</li>`;
}).join('');
}
// Category chart
renderCatChart(expenses);
}
function renderCatChart(data) {
const totals = {};
data.forEach(e => { totals[e.cat] = (totals[e.cat] || 0) + e.amount; });
const grand = Object.values(totals).reduce((s, v) => s + v, 0);
const sorted = Object.entries(totals).sort((a, b) => b[1] - a[1]);
if (sorted.length === 0) {
$('cat-chart').innerHTML = '<div class="empty-msg">No data yet.</div>';
return;
}
$('cat-chart').innerHTML = sorted.map(([cat, val]) => {
const c = CATS[cat] || CATS.other;
const pct = grand > 0 ? (val / grand * 100).toFixed(1) : 0;
return `
<div class="cat-row">
<div class="cat-label">${c.icon} ${cat}</div>
<div class="cat-bar-wrap">
<div class="cat-bar" style="width:${pct}%;background:${c.color}"></div>
</div>
<div class="cat-val" style="color:${c.color}">${fmtMoney(val)}</div>
</div>`;
}).join('');
}
// ============================================================
// TO-DO LIST
// ============================================================
let todos = JSON.parse(localStorage.getItem('dp_todos') || '[]');
let todoFilter = 'all';
function saveTodos() { localStorage.setItem('dp_todos', JSON.stringify(todos)); }
function addTodo() {
const text = $('todo-text').value.trim();
const priority = $('todo-priority').value;
const due = $('todo-due').value;
if (!text) return toast('Enter a task description', '#ef4444');
todos.unshift({ id: Date.now(), text, priority, due, done: false, createdAt: todayStr() });
saveTodos();
$('todo-text').value = '';
$('todo-due').value = '';
renderTodos();
toast('Task added!', '#22c55e');
}
function toggleTodo(id) {
const t = todos.find(t => t.id === id);
if (t) { t.done = !t.done; saveTodos(); renderTodos(); }
}
function deleteTodo(id) {
todos = todos.filter(t => t.id !== id);
saveTodos();
renderTodos();
toast('Task removed', '#ef4444');
}
function clearDoneTodos() {
const count = todos.filter(t => t.done).length;
if (count === 0) return toast('No completed tasks to clear');
todos = todos.filter(t => !t.done);
saveTodos();
renderTodos();
toast(`${count} task(s) cleared`);
}
function setTodoFilter(f, btn) {
todoFilter = f;
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
renderTodos();
}
function renderTodos() {
// Progress
const total = todos.length;
const done = todos.filter(t => t.done).length;
$('todo-progress-text').textContent = `${done} of ${total} tasks completed`;
const pct = total > 0 ? (done / total * 100) : 0;
$('todo-progress-fill').style.width = pct + '%';
// Filter
const filtered = todos.filter(t => {
if (todoFilter === 'active') return !t.done;
if (todoFilter === 'done') return t.done;
if (todoFilter === 'high') return t.priority === 'high' && !t.done;
return true;
});
const PRIORITY_LABELS = { high: '๐ด High', medium: '๐ก Medium', low: '๐ข Low' };
const PRIORITY_CLASS = { high: 'p-high', medium: 'p-medium', low: 'p-low' };
const ul = $('todo-list');
if (filtered.length === 0) {
ul.innerHTML = '<li><div class="empty-msg">No tasks here.</div></li>';
return;
}
// Sort: undone high first, then medium, low; done at bottom
const order = { high: 0, medium: 1, low: 2 };
const sorted = [...filtered].sort((a, b) => {
if (a.done !== b.done) return a.done ? 1 : -1;
return order[a.priority] - order[b.priority];
});
ul.innerHTML = sorted.map(t => {
const dueLabel = t.due ? `<div class="todo-date-label">๐
Due: ${formatDate(t.due)}</div>` : '';
const overdue = t.due && t.due < todayStr() && !t.done ? '<span style="color:var(--red);font-size:0.72rem"> โ Overdue</span>' : '';
return `
<li class="todo-item">
<div class="todo-check ${t.done ? 'done' : ''}" onclick="toggleTodo(${t.id})"></div>
<div class="todo-text-wrap">
<div class="todo-text ${t.done ? 'done' : ''}">${escHtml(t.text)}${overdue}</div>
<div class="todo-priority ${PRIORITY_CLASS[t.priority]}">${PRIORITY_LABELS[t.priority]}</div>
${dueLabel}
</div>
<div class="todo-actions">
<button class="btn btn-danger" onclick="deleteTodo(${t.id})">โ</button>
</div>
</li>`;
}).join('');
}
// ============================================================
// UTILS
// ============================================================
function escHtml(s) {
return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
}
// Allow Enter key to submit
$('exp-name').addEventListener('keydown', e => { if (e.key === 'Enter') addExpense(); });
$('exp-amount').addEventListener('keydown', e => { if (e.key === 'Enter') addExpense(); });
$('todo-text').addEventListener('keydown', e => { if (e.key === 'Enter') addTodo(); });
// ============================================================
// INIT
// ============================================================
$('exp-date').value = todayStr();
$('todo-due').value = '';
renderExpenses();
renderTodos();
</script>
</body>
</html>
ls -lh /home/daytona/workspace/daily-app/index.html
index
HTML File ยท HTML
| Feature | Details |
|---|---|
| Add expenses | Name, amount, category, date |
| 8 categories | Food, Transport, Shopping, Health, Bills, Entertainment, Education, Other |
| Summary cards | Today's spend, This Month, All Time |
| Category chart | Visual bar chart showing spending breakdown |
| Date filter | Filter expense log by date range |
| Delete / Clear All | Remove individual or all expenses |
| Feature | Details |
|---|---|
| Add tasks | Description, priority level, due date |
| 3 priorities | High (๐ด), Medium (๐ก), Low (๐ข) |
| Overdue warning | Tasks past due date get a red โ tag |
| Progress bar | Shows how many tasks completed |
| Filter tabs | All / Active / Done / High Priority |
| Toggle done | Click the circle to mark complete |
| Clear Done | Remove all completed tasks at once |
Enter to quickly add expenses or tasks