<?php
declare(strict_types=1);
require_once 'includes/config.php';
require_once 'includes/functions.php';
$user = require_login();
/* ── Handle POST actions ──────────────────────────────────────── */
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!verify_csrf()) {
flash('error', 'Invalid request token.');
redirect('dashboard.php');
}
$action = $_POST['action'] ?? '';
if ($action === 'new_list') {
$title = trim($_POST['title'] ?? '');
$desc = trim($_POST['description'] ?? '');
$pub = isset($_POST['public']) && $_POST['public'] === '1';
if ($title === '') {
flash('error', 'List title cannot be empty.');
} else {
$list = create_list($user['username'], $title, $desc, $pub);
flash('success', 'List "' . $title . '" created.');
redirect(list_url($user['username'], $list['id']));
}
}
if ($action === 'delete_list') {
$list_id = $_POST['list_id'] ?? '';
$list = get_list($user['username'], $list_id);
if ($list && can_edit_list($list, $user)) {
delete_list($user['username'], $list_id);
flash('success', 'List deleted.');
}
}
redirect('dashboard.php');
}
/* ── Load data ────────────────────────────────────────────────── */
$lists = get_lists_for_user($user['username']);
$page_title = 'My Lists';
require_once 'includes/header.php';
?>
<div class="page-wrap-wide">
<!-- ── New list form ──────────────────────────────────── -->
<div class="window mt-10">
<div class="window-title"><span>New To-Do List</span></div>
<div class="window-body">
<form method="post" action="dashboard.php">
<?= csrf_field() ?>
<input type="hidden" name="action" value="new_list">
<div class="form-row-inline">
<div class="stretch">
<label for="new_title">List Title</label>
<input type="text" id="new_title" name="title" maxlength="128"
placeholder="My new to-do list…" required>
</div>
<div style="min-width:200px;">
<label for="new_desc">Description <span style="font-weight:normal;font-size:11px;">(optional)</span></label>
<input type="text" id="new_desc" name="description" maxlength="256"
placeholder="What is this list for?">
</div>
<div style="padding-top:18px;">
<div class="checkbox-row">
<input type="checkbox" id="new_public" name="public" value="1">
<label for="new_public">Public</label>
</div>
</div>
<div style="padding-top:18px;">
<button type="submit" class="btn btn-primary">Create List</button>
</div>
</div>
</form>
</div>
</div>
<!-- ── Import / Export toolbar ───────────────────────── -->
<div style="margin-top:8px; display:flex; gap:6px; flex-wrap:wrap; align-items:center;">
<a href="import.php" class="btn">⇓ Import Tasks</a>
<?php if (!empty($lists)): ?>
<a href="export.php?format=zip&owner=<?= h($user['username']) ?>"
class="btn" title="Download all lists as ZIP (CSV + MD for each)">💾 Download All as ZIP</a>
<?php endif; ?>
</div>
<!-- ── List directory ─────────────────────────────────── -->
<div class="window mt-10" style="margin-bottom:16px;">
<div class="window-title">
<span>My Lists</span>
<span style="font-size:11px;opacity:.8;"><?= count($lists) ?> list<?= count($lists) !== 1 ? 's' : '' ?></span>
</div>
<div class="window-body">
<?php if (empty($lists)): ?>
<div class="text-center" style="padding:24px 0; color:var(--chrome-dark);">
No lists yet. Create your first list above.
</div>
<?php else: ?>
<!-- Filter / sort bar -->
<div style="margin-bottom:8px; display:flex; gap:8px; flex-wrap:wrap; align-items:center;">
<span class="section-title" style="margin:0;">
<?= count(array_filter($lists, fn($l) => !($l['public'] ?? false))) ?> private |
<?= count(array_filter($lists, fn($l) => ($l['public'] ?? false))) ?> public
</span>
</div>
<div class="list-grid">
<?php foreach ($lists as $lst): ?>
<div class="list-card">
<div class="list-card-title">
<a href="<?= h(list_url($user['username'], $lst['id'])) ?>"><?= h($lst['title']) ?></a>
<?php if ($lst['public']): ?>
<span class="badge badge-pub">PUBLIC</span>
<?php else: ?>
<span class="badge badge-priv">PRIVATE</span>
<?php endif; ?>
</div>
<div class="list-card-body">
<?= $lst['description'] ? h($lst['description']) : '<em style="color:var(--chrome-dark)">No description</em>' ?>
</div>
<div class="list-card-footer">
<span class="task-counts">
<span title="Active tasks" style="color:var(--normal);">■ <?= $lst['active_count'] ?></span>
<span title="Completed tasks" style="color:var(--low);">✓ <?= $lst['completed_count'] ?></span>
</span>
<?php $lst_hits = get_list_hits($lst['id']); ?>
<span style="font-size:10px; color:var(--chrome-dark);"
title="Total views · unique visitors">
👁 <?= number_format($lst_hits['total']) ?>
· <?= number_format($lst_hits['unique']) ?>u
</span>
<span style="font-size:10px;color:var(--chrome-dark);">
<?= date('d M Y', strtotime($lst['updated'])) ?>
</span>
<div class="d-flex gap-4" style="flex-wrap:wrap;">
<a href="<?= h(list_url($user['username'], $lst['id'])) ?>" class="btn btn-sm">Open</a>
<a href="<?= h(list_url($user['username'], $lst['id'], ['edit' => '1'])) ?>" class="btn btn-sm">Settings</a>
<a href="export.php?format=csv&owner=<?= h($user['username']) ?>&id=<?= h($lst['id']) ?>"
class="btn btn-sm" title="Download CSV">CSV</a>
<a href="export.php?format=md&owner=<?= h($user['username']) ?>&id=<?= h($lst['id']) ?>"
class="btn btn-sm" title="Download Markdown">MD</a>
<a href="print.php?id=<?= h($lst['id']) ?>&owner=<?= h($user['username']) ?>"
class="btn btn-sm" target="_blank">Print</a>
</div>
<form method="post" action="dashboard.php"
onsubmit="return confirm('Delete list: <?= h(addslashes($lst['title'])) ?>?')">
<?= csrf_field() ?>
<input type="hidden" name="action" value="delete_list">
<input type="hidden" name="list_id" value="<?= h($lst['id']) ?>">
<button type="submit" class="btn btn-sm btn-danger" title="Delete list">✗</button>
</form>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php require_once 'includes/footer.php'; ?>