simpleLedger/index.html
2025-11-06 00:32:05 -05:00

581 lines
41 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SimpleLedger - Your Personal Bookkeeper</title>
<link rel="stylesheet" href="style.css">
<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@400;500;600;700&display=swap" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.1/dist/chart.min.js"></script>
<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
<style>
body { font-family: 'Inter', sans-serif; }
@keyframes spin {
to { transform: rotate(360deg); }
}
.spinner {
border: 4px solid rgba(0, 0, 0, .1);
border-left-color: #6366f1;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
}
/* Modal base */
.modal {
transition: opacity 0.25s ease;
}
.modal-content {
transition: transform 0.25s ease;
}
/* Sortable table header */
.sortable-header {
cursor: pointer;
user-select: none;
}
.sortable-header:hover {
background-color: rgba(148, 163, 184, 0.16);
color: #2563eb;
}
body.dark-mode .sortable-header:hover {
background-color: rgba(30, 41, 59, 0.8);
color: #93c5fd;
}
.sortable-header.sort-asc::after {
content: ' ▲';
font-size: 0.8em;
}
.sortable-header.sort-desc::after {
content: ' ▼';
font-size: 0.8em;
}
</style>
</head>
<body class="bg-slate-100 text-slate-900 dark:bg-gray-950 dark:text-slate-100">
<div id="authView" class="flex min-h-screen items-center justify-center bg-gradient-to-br from-slate-900 via-blue-900 to-indigo-900 px-4 py-16">
<div class="card w-full max-w-md bg-white/90 backdrop-blur-md dark:bg-gray-900/90 dark:ring-1 dark:ring-white/10">
<div class="mb-6 text-center">
<span class="inline-flex items-center rounded-full bg-blue-100 px-3 py-1 text-xs font-semibold uppercase tracking-[0.3em] text-blue-600 dark:bg-blue-500/20 dark:text-blue-300">SimpleLedger</span>
<h2 id="authTitle" class="mt-4 text-3xl font-semibold text-gray-900 dark:text-white">Login</h2>
<p class="mt-2 text-sm text-slate-500 dark:text-slate-400">Track income, tame expenses, celebrate wins.</p>
</div>
<div id="authError" class="mb-4 hidden rounded-lg border border-red-100 bg-red-50 px-4 py-3 text-sm text-red-600 dark:border-red-500/40 dark:bg-red-500/10"></div>
<form id="loginForm" class="space-y-4">
<div>
<label for="loginUsername" class="block text-sm font-medium text-slate-600 dark:text-slate-300">Username</label>
<input type="text" id="loginUsername" name="username" required class="form-control mt-1 focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div>
<label for="loginPassword" class="block text-sm font-medium text-slate-600 dark:text-slate-300">Password</label>
<input type="password" id="loginPassword" name="password" required class="form-control mt-1 focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<button type="submit" class="btn btn-primary w-full bg-gradient-to-r from-blue-600 via-indigo-500 to-purple-500 text-white shadow-lg shadow-indigo-500/30 transition hover:-translate-y-0.5 hover:shadow-xl">
Login
</button>
</form>
<form id="registerForm" class="hidden space-y-4">
<div>
<label for="regUsername" class="block text-sm font-medium text-slate-600 dark:text-slate-300">Username</label>
<input type="text" id="regUsername" name="username" required class="form-control mt-1 focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div>
<label for="regPassword" class="block text-sm font-medium text-slate-600 dark:text-slate-300">Password</label>
<input type="password" id="regPassword" name="password" required class="form-control mt-1 focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<button type="submit" class="btn btn-primary w-full bg-gradient-to-r from-blue-600 via-indigo-500 to-purple-500 text-white shadow-lg shadow-indigo-500/30 transition hover:-translate-y-0.5 hover:shadow-xl">
Register
</button>
</form>
<p class="mt-6 text-center text-sm text-slate-500 dark:text-slate-400">
<a href="#" id="toggleAuth" class="font-semibold text-blue-500 hover:underline">Need an account? Register</a>
</p>
</div>
</div>
<div id="loadingModal" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-slate-900/60 backdrop-blur">
<div class="card flex items-center gap-4 bg-white/90 text-slate-700 shadow-2xl dark:bg-slate-900/90 dark:text-slate-100">
<div class="spinner mr-1"></div>
<span id="loadingModalText" class="text-lg font-semibold">Loading...</span>
</div>
</div>
<div id="editTxModal" class="modal hidden fixed inset-0 z-40 flex items-center justify-center bg-slate-900/60 backdrop-blur">
<div class="modal-content card w-full max-w-md mx-4 space-y-4">
<input type="hidden" id="splitTxId">
<input type="hidden" id="splitTxTotal">
<div class="flex items-center justify-between">
<div>
<h3 class="text-xl font-semibold text-slate-900 dark:text-white" id="splitModalTitle">Split Transaction</h3>
<p class="text-sm text-slate-500 dark:text-slate-300" id="splitModalDescription"></p>
</div>
<div class="text-right">
<div class="text-lg font-bold text-slate-900 dark:text-white" id="splitModalTotal"></div>
<div class="text-sm font-medium text-slate-500 dark:text-slate-300" id="splitModalRemaining"></div>
</div>
</div>
<div id="splitsContainer" class="max-h-96 space-y-3 overflow-y-auto rounded-xl bg-slate-50/70 p-3 dark:bg-slate-800/60">
</div>
<div class="flex items-center justify-between">
<div>
<button type="button" id="addSplitBtn" class="btn btn-primary bg-gradient-to-r from-emerald-500 to-teal-500 px-4 py-2 text-sm text-white shadow-lg shadow-emerald-500/30 hover:-translate-y-0.5 hover:shadow-xl">
Add Split
</button>
<button type="button" id="createRuleFromSplit" class="btn btn-primary bg-gradient-to-r from-indigo-500 to-purple-500 px-4 py-2 text-sm text-white shadow-lg shadow-indigo-500/30 hover:-translate-y-0.5 hover:shadow-xl">
Create Rule
</button>
</div>
<div class="flex space-x-3">
<button type="button" id="closeTxModalBtn" class="btn bg-white/80 px-4 py-2 text-sm font-semibold text-slate-600 hover:-translate-y-0.5 hover:shadow-md dark:bg-slate-800/70 dark:text-slate-200">
Cancel
</button>
<button type="button" id="saveSplitsBtn" class="btn btn-primary bg-gradient-to-r from-blue-500 via-indigo-500 to-purple-500 px-4 py-2 text-sm text-white shadow-lg shadow-indigo-500/30 hover:-translate-y-0.5 hover:shadow-xl disabled:cursor-not-allowed disabled:opacity-50">
Save Changes
</button>
</div>
</div>
</div>
</div>
<div id="appContainer" class="hidden min-h-screen bg-slate-100 dark:bg-gray-950">
<div class="relative flex min-h-screen w-full overflow-hidden">
<div id="mobileOverlay" class="fixed inset-0 z-30 hidden bg-slate-900/50 backdrop-blur-sm lg:hidden"></div>
<aside id="sidebar" class="fixed inset-y-0 left-0 z-40 flex w-72 -translate-x-full transform flex-col gap-6 overflow-y-auto border-r border-white/40 bg-white/95 px-4 py-8 shadow-2xl transition-transform duration-300 ease-out dark:border-gray-800 dark:bg-gray-900/95 lg:static lg:translate-x-0 lg:shadow-none">
<div class="flex items-start justify-between gap-4 px-1">
<div>
<span class="text-xs font-semibold uppercase tracking-[0.4em] text-blue-500">SimpleLedger</span>
<h1 class="mt-3 text-2xl font-bold text-slate-900 dark:text-white">Money Magic</h1>
<p class="mt-1 text-sm text-slate-500 dark:text-slate-400">Your playful finance HQ</p>
</div>
<button id="closeSidebar" class="inline-flex h-10 w-10 items-center justify-center rounded-full bg-slate-100 text-slate-500 shadow-sm transition hover:-translate-y-0.5 hover:bg-slate-200 hover:text-slate-700 dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-700 lg:hidden" aria-label="Close navigation">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<nav class="flex-1 space-y-2">
<a href="#" data-view="dashboard" class="nav-link">
<span class="nav-icon bg-blue-500/10 text-blue-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M3 9.75L12 4l9 5.75M21 9.75V19a2 2 0 01-2 2h-2.25a.75.75 0 01-.75-.75V14a2 2 0 00-2-2h-3a2 2 0 00-2 2v6.25a.75.75 0 01-.75.75H6a2 2 0 01-2-2V9.75" />
</svg>
</span>
<span>Dashboard</span>
</a>
<a href="#" data-view="transactions" class="nav-link">
<span class="nav-icon bg-indigo-500/10 text-indigo-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M3.75 7.5h16.5M3.75 12h16.5M3.75 16.5h10.5" />
</svg>
</span>
<span>Transactions</span>
</a>
<a href="#" data-view="upload" class="nav-link">
<span class="nav-icon bg-emerald-500/10 text-emerald-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 4v12m0 0l-4-4m4 4 4-4M6 20h12" />
</svg>
</span>
<span>Upload</span>
</a>
<a href="#" data-view="categories" class="nav-link">
<span class="nav-icon bg-amber-500/10 text-amber-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M3.75 5.25h16.5M5.25 5.25V18a2.25 2.25 0 002.25 2.25h9a2.25 2.25 0 002.25-2.25V5.25" />
</svg>
</span>
<span>Categories</span>
</a>
<a href="#" data-view="vendors" class="nav-link">
<span class="nav-icon bg-fuchsia-500/10 text-fuchsia-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4.5 9.75l7.5-6 7.5 6M19.5 9.75V18a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 18V9.75" />
</svg>
</span>
<span>Vendors</span>
</a>
<a href="#" data-view="rules" class="nav-link">
<span class="nav-icon bg-purple-500/10 text-purple-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M8.25 4.5h10.5m-10.5 0A2.25 2.25 0 006 6.75v10.5a2.25 2.25 0 002.25 2.25h10.5A2.25 2.25 0 0021 17.25V6.75A2.25 2.25 0 0018.75 4.5m-10.5 0V3.75A1.125 1.125 0 019.375 2.625h7.875A1.125 1.125 0 0118.375 3.75V4.5m-10.5 0h10.5" />
</svg>
</span>
<span>Rules</span>
</a>
<a href="#" data-view="reports" class="nav-link">
<span class="nav-icon bg-rose-500/10 text-rose-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M3 12h3l2-5 4 10 3-6h6" />
</svg>
</span>
<span>Reports</span>
</a>
</nav>
<div class="space-y-3 border-t border-slate-200 pt-4 dark:border-slate-800">
<button id="darkModeToggle" class="flex w-full items-center justify-between rounded-xl bg-slate-900 px-4 py-3 text-sm font-semibold text-white shadow-lg shadow-slate-900/30 transition hover:-translate-y-0.5 hover:shadow-xl dark:bg-slate-200 dark:text-slate-900 dark:shadow-slate-200/40">
<span>Toggle dark mode</span>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 3v1.5m0 15V21m9-9h-1.5M4.5 12H3m15.364 6.364l-1.06-1.06M7.696 7.696 6.636 6.636m11.314 0-1.06 1.06M7.696 16.304l-1.06 1.06M16.5 12a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0z" />
</svg>
</button>
<a href="#" id="logoutButton" class="flex items-center justify-between rounded-xl bg-white/90 px-4 py-3 text-sm font-semibold text-slate-600 shadow-inner transition hover:-translate-y-0.5 hover:bg-white dark:bg-slate-800/80 dark:text-slate-200 dark:hover:bg-slate-800">
<span>Logout</span>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6A2.25 2.25 0 005.25 5.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0 3-3m0 0-3-3m3 3H9" />
</svg>
</a>
</div>
</aside>
<div class="flex flex-1 flex-col lg:ml-72">
<header class="sticky top-0 z-20 flex items-center justify-between gap-4 border-b border-slate-200/60 bg-slate-100/80 px-4 py-4 backdrop-blur-md dark:border-slate-800/60 dark:bg-gray-950/80 sm:px-6 lg:px-10">
<div class="flex items-center gap-3">
<button id="menuToggle" class="inline-flex h-11 w-11 items-center justify-center rounded-full bg-white text-slate-600 shadow-lg shadow-slate-400/40 transition hover:-translate-y-0.5 hover:text-slate-900 dark:bg-slate-800 dark:text-slate-300 dark:shadow-none lg:hidden" aria-label="Open navigation">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 6h16M4 12h16M4 18h10" />
</svg>
</button>
<div>
<p class="text-xs font-semibold uppercase tracking-[0.3em] text-slate-500 dark:text-slate-400">Welcome back</p>
<h2 class="mt-1 text-2xl font-bold text-slate-900 dark:text-white">Let's balance the books</h2>
</div>
</div>
<div class="flex flex-wrap items-center gap-2">
<button id="quickTransactions" class="rounded-full border border-transparent bg-white px-4 py-2 text-sm font-semibold text-slate-700 shadow-sm transition hover:-translate-y-0.5 hover:bg-slate-100 dark:bg-slate-800 dark:text-slate-200 dark:hover:bg-slate-700">Transactions</button>
<button id="quickUpload" class="rounded-full bg-gradient-to-r from-blue-500 via-indigo-500 to-purple-500 px-4 py-2 text-sm font-semibold text-white shadow-lg shadow-indigo-500/30 transition hover:-translate-y-0.5 hover:shadow-xl">Upload CSVs</button>
</div>
</header>
<main class="flex-1 overflow-y-auto px-4 pb-16 pt-8 sm:px-6 lg:px-10">
<section id="dashboard" class="space-y-6">
<div>
<h2 class="text-3xl font-semibold text-slate-900 dark:text-white">Dashboard</h2>
<p class="mt-2 text-sm text-slate-500 dark:text-slate-400">Get a pulse on your business at a glance.</p>
</div>
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5">
<div class="card stat-card">
<div class="stat-icon bg-emerald-500/15 text-emerald-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 16l5-5 3 3 8-8" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 20h16" />
</svg>
</div>
<div>
<h3 class="stat-label">Gross Income</h3>
<p id="statGrossIncome" class="stat-value text-emerald-500">$0.00</p>
</div>
</div>
<div class="card stat-card">
<div class="stat-icon bg-amber-500/15 text-amber-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M8 7h8a3 3 0 110 6H8" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M10.5 10.5L8 13l2.5 2.5" />
</svg>
</div>
<div>
<h3 class="stat-label">Returns &amp; Discounts</h3>
<p id="statReturns" class="stat-value text-amber-500">$0.00</p>
</div>
</div>
<div class="card stat-card">
<div class="stat-icon bg-green-500/15 text-green-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 19V5" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M8 9l4-4 4 4" />
</svg>
</div>
<div>
<h3 class="stat-label">Net Income</h3>
<p id="statNetIncome" class="stat-value text-green-500">$0.00</p>
</div>
</div>
<div class="card stat-card">
<div class="stat-icon bg-rose-500/15 text-rose-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 5v14" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M16 15l-4 4-4-4" />
</svg>
</div>
<div>
<h3 class="stat-label">Total Expenses</h3>
<p id="statTotalExpenses" class="stat-value text-rose-500">$0.00</p>
</div>
</div>
<div class="card stat-card">
<div class="stat-icon bg-indigo-500/15 text-indigo-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 5v14" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6 9h12" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M7.5 9L5 13h5L7.5 9z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M16.5 9L14 13h5l-2.5-4z" />
</svg>
</div>
<div>
<h3 class="stat-label">Net Profit / (Loss)</h3>
<p id="statNetProfit" class="stat-value">$0.00</p>
</div>
</div>
</div>
<div class="card h-96 space-y-4">
<div class="flex items-center justify-between">
<h3 class="text-xl font-semibold text-slate-900 dark:text-slate-100">Top 10 Expenses</h3>
<span class="rounded-full bg-slate-100 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-slate-500 dark:bg-slate-800/70 dark:text-slate-300">Live view</span>
</div>
<div class="relative h-full w-full max-h-[320px]">
<canvas id="expensePieChart"></canvas>
</div>
</div>
</section>
<section id="transactions" class="hidden space-y-6">
<div class="card space-y-4">
<div class="flex flex-wrap items-center justify-between gap-4">
<h2 class="text-2xl font-semibold text-slate-900 dark:text-white">Transactions</h2>
<p class="text-sm text-slate-500 dark:text-slate-400">Filter, search, and split with ease.</p>
</div>
<div class="flex flex-col gap-4 md:flex-row md:flex-wrap md:items-end">
<div class="w-full min-w-[180px] md:w-auto">
<label for="dateFilterSelect" class="text-sm font-medium text-slate-600 dark:text-slate-300">Date Range</label>
<select id="dateFilterSelect" class="form-control mt-1">
<option value="all">All Time</option>
<option value="lastMonth" selected>Last Month</option>
<option value="custom">Custom</option>
</select>
</div>
<div id="customDateRangePicker" class="hidden w-full flex-wrap items-end gap-4 md:w-auto">
<div class="w-full min-w-[160px] md:w-auto">
<label for="startDate" class="text-sm font-medium text-slate-600 dark:text-slate-300">Start Date</label>
<input type="date" id="startDate" class="form-control mt-1">
</div>
<div class="w-full min-w-[160px] md:w-auto">
<label for="endDate" class="text-sm font-medium text-slate-600 dark:text-slate-300">End Date</label>
<input type="date" id="endDate" class="form-control mt-1">
</div>
</div>
<div class="w-full min-w-[200px] md:flex-1">
<label for="transactionSearch" class="text-sm font-medium text-slate-600 dark:text-slate-300">Search</label>
<input type="search" id="transactionSearch" placeholder="Descriptions..." class="form-control mt-1">
</div>
<div class="w-full min-w-[160px] md:w-auto">
<label for="transactionFilter" class="text-sm font-medium text-slate-600 dark:text-slate-300">Status</label>
<select id="transactionFilter" class="form-control mt-1">
<option value="all">All</option>
<option value="uncategorized">Uncategorized</option>
</select>
</div>
<div class="w-full md:hidden">
<label for="mobileSortSelect" class="text-sm font-medium text-slate-600 dark:text-slate-300">Sort By</label>
<select id="mobileSortSelect" class="form-control mt-1">
<option value="date-desc" selected>Newest first</option>
<option value="date-asc">Oldest first</option>
<option value="amount-desc">Amount (high -> low)</option>
<option value="amount-asc">Amount (low -> high)</option>
<option value="description-asc">Description (A -> Z)</option>
<option value="description-desc">Description (Z -> A)</option>
<option value="category-asc">Category (A -> Z)</option>
<option value="category-desc">Category (Z -> A)</option>
<option value="vendorName-asc">Vendor (A -> Z)</option>
<option value="vendorName-desc">Vendor (Z -> A)</option>
</select>
</div>
</div>
</div>
<div id="transactionsListMobile" class="space-y-4 md:hidden">
</div>
<div class="card hidden overflow-hidden md:block">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-slate-200 text-left text-sm dark:divide-slate-800">
<thead class="bg-slate-50/80 text-xs font-semibold uppercase tracking-wide text-slate-500 dark:bg-slate-900/40 dark:text-slate-300">
<tr>
<th class="sortable-header px-4 py-3" data-sort="date">Date</th>
<th class="sortable-header px-4 py-3" data-sort="description">Description</th>
<th class="sortable-header px-4 py-3" data-sort="category">Category</th>
<th class="sortable-header px-4 py-3" data-sort="vendorName">Vendor</th>
<th class="sortable-header px-4 py-3 text-right" data-sort="amount">Amount</th>
<th class="px-4 py-3 text-center">Actions</th>
</tr>
</thead>
<tbody id="transactionsTableBody" class="divide-y divide-slate-100 dark:divide-slate-800">
</tbody>
</table>
</div>
</div>
</section>
<section id="upload" class="hidden space-y-6">
<div>
<h2 class="text-3xl font-semibold text-slate-900 dark:text-white">Upload Files</h2>
<p class="mt-2 text-sm text-slate-500 dark:text-slate-400">Keep your books fresh by dropping in your latest CSV exports.</p>
</div>
<form id="uploadForm" class="card max-w-2xl space-y-6">
<div class="space-y-2">
<label for="bankFile" class="block text-sm font-medium text-slate-600 dark:text-slate-300">1. Bank Transactions CSV</label>
<p class="text-xs text-slate-500 dark:text-slate-400">(e.g., your `transactions.csv` file. Must have 'Date', 'Description', 'Debit', 'Credit' columns)</p>
<input type="file" id="bankFile" accept=".csv" class="form-control cursor-pointer bg-white/60 file:mr-4 file:rounded-full file:border-0 file:bg-blue-500 file:px-4 file:py-2 file:text-sm file:font-semibold file:text-white hover:file:bg-blue-600 dark:bg-slate-800/60">
</div>
<div class="space-y-2">
<label for="salesFile" class="block text-sm font-medium text-slate-600 dark:text-slate-300">2. Sales Summary CSV</label>
<p class="text-xs text-slate-500 dark:text-slate-400">(e.g., your `sales-summary.csv` file. Used to find 'Net sales' and 'Square payment processing fees')</p>
<input type="file" id="salesFile" accept=".csv" class="form-control cursor-pointer bg-white/60 file:mr-4 file:rounded-full file:border-0 file:bg-indigo-500 file:px-4 file:py-2 file:text-sm file:font-semibold file:text-white hover:file:bg-indigo-600 dark:bg-slate-800/60">
</div>
<button type="submit" class="btn btn-primary w-full bg-gradient-to-r from-blue-500 via-indigo-500 to-purple-500 py-3 text-white shadow-lg shadow-indigo-500/30 transition hover:-translate-y-0.5 hover:shadow-xl">
Process Files
</button>
</form>
</section>
<section id="categories" class="hidden space-y-6">
<div>
<h2 class="text-3xl font-semibold text-slate-900 dark:text-white">Manage Categories</h2>
<p class="mt-2 text-sm text-slate-500 dark:text-slate-400">Build the buckets that make sense for your world.</p>
</div>
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
<div class="card space-y-4">
<h3 class="text-xl font-semibold text-slate-900 dark:text-white">Add New Category</h3>
<form id="categoryForm" class="space-y-4">
<div class="space-y-2">
<label for="categoryName" class="block text-sm font-medium text-slate-600 dark:text-slate-300">Name</label>
<input type="text" id="categoryName" name="categoryName" required class="form-control">
</div>
<div class="space-y-2">
<label for="categoryType" class="block text-sm font-medium text-slate-600 dark:text-slate-300">Type</label>
<select id="categoryType" name="categoryType" class="form-control">
<option value="income">Income</option>
<option value="expense">Expense</option>
<option value="liability">Liability</option>
</select>
</div>
<button type="submit" class="btn btn-primary w-full bg-gradient-to-r from-blue-500 to-indigo-500 py-2 text-white shadow-lg shadow-indigo-500/30 transition hover:-translate-y-0.5 hover:shadow-xl">
Add Category
</button>
</form>
</div>
<div class="card space-y-4">
<h3 class="text-xl font-semibold text-slate-900 dark:text-white">Existing Categories</h3>
<div id="categoryList" class="space-y-2 max-h-96 overflow-y-auto">
</div>
</div>
</div>
</section>
<section id="vendors" class="hidden space-y-6">
<div>
<h2 class="text-3xl font-semibold text-slate-900 dark:text-white">Vendors</h2>
<p class="mt-2 text-sm text-slate-500 dark:text-slate-400">Keep track of who helps your business shine.</p>
</div>
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
<div class="card space-y-4">
<h3 class="text-xl font-semibold text-slate-900 dark:text-white">Add New Vendor</h3>
<form id="vendorForm" class="space-y-4">
<div class="space-y-2">
<label for="vendorName" class="block text-sm font-medium text-slate-600 dark:text-slate-300">Vendor Name</label>
<input type="text" id="vendorName" name="vendorName" required class="form-control">
</div>
<div class="space-y-2">
<label for="vendorContact" class="block text-sm font-medium text-slate-600 dark:text-slate-300">Contact Info</label>
<input type="text" id="vendorContact" name="vendorContact" class="form-control">
</div>
<button type="submit" class="btn btn-primary w-full bg-gradient-to-r from-blue-500 to-indigo-500 py-2 text-white shadow-lg shadow-indigo-500/30 transition hover:-translate-y-0.5 hover:shadow-xl">
Add Vendor
</button>
</form>
</div>
<div class="card space-y-4">
<h3 class="text-xl font-semibold text-slate-900 dark:text-white">Existing Vendors</h3>
<div id="vendorList" class="space-y-2 max-h-96 overflow-y-auto">
</div>
</div>
</div>
</section>
<section id="rules" class="hidden space-y-6">
<div class="flex flex-wrap items-center justify-between gap-4">
<div>
<h2 class="text-3xl font-semibold text-slate-900 dark:text-white">Categorization Rules</h2>
<p class="mt-1 text-sm text-slate-500 dark:text-slate-400">Teach SimpleLedger how to sort new transactions automatically.</p>
</div>
<button id="applyAllRulesBtn" class="btn btn-primary bg-gradient-to-r from-emerald-500 to-teal-500 px-4 py-2 text-sm font-semibold text-white shadow-lg shadow-emerald-500/30 transition hover:-translate-y-0.5 hover:shadow-xl">
Apply Rules to Uncategorized
</button>
</div>
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
<div class="card space-y-4">
<h3 class="text-xl font-semibold text-slate-900 dark:text-white">Add New Rule</h3>
<form id="ruleForm" class="space-y-4">
<div class="space-y-2">
<label for="ruleKeyword" class="block text-sm font-medium text-slate-600 dark:text-slate-300">If 'Description' contains:</label>
<input type="text" id="ruleKeyword" name="ruleKeyword" placeholder="e.g., ADP PAYROLL" required class="form-control">
</div>
<div class="space-y-2">
<label for="ruleCategory" class="block text-sm font-medium text-slate-600 dark:text-slate-300">Set Category to:</label>
<select id="ruleCategory" name="ruleCategory" class="category-select form-control">
</select>
</div>
<button type="submit" class="btn btn-primary w-full bg-gradient-to-r from-blue-500 via-indigo-500 to-purple-500 py-2 text-white shadow-lg shadow-indigo-500/30 transition hover:-translate-y-0.5 hover:shadow-xl">
Add Rule
</button>
</form>
</div>
<div class="card space-y-4">
<h3 class="text-xl font-semibold text-slate-900 dark:text-white">Existing Rules</h3>
<div id="rulesList" class="space-y-2 max-h-96 overflow-y-auto">
</div>
</div>
</div>
</section>
<section id="reports" class="hidden space-y-6">
<div>
<h2 class="text-3xl font-semibold text-slate-900 dark:text-white">Reports</h2>
<p class="mt-2 text-sm text-slate-500 dark:text-slate-400">Build your Profit &amp; Loss in seconds.</p>
</div>
<div class="card space-y-6">
<div class="flex flex-wrap items-end gap-4">
<div class="min-w-[160px] space-y-2">
<label for="reportStartDate" class="block text-sm font-medium text-slate-600 dark:text-slate-300">Start Date</label>
<input type="date" id="reportStartDate" class="form-control">
</div>
<div class="min-w-[160px] space-y-2">
<label for="reportEndDate" class="block text-sm font-medium text-slate-600 dark:text-slate-300">End Date</label>
<input type="date" id="reportEndDate" class="form-control">
</div>
<button id="generateReportBtn" class="btn btn-primary bg-gradient-to-r from-blue-500 to-indigo-500 px-4 py-2 text-sm font-semibold text-white shadow-lg shadow-indigo-500/30 transition hover:-translate-y-0.5 hover:shadow-xl">
Generate
</button>
<button id="exportReportBtn" class="hidden btn bg-gradient-to-r from-emerald-500 to-teal-500 px-4 py-2 text-sm font-semibold text-white shadow-lg shadow-emerald-500/30 transition hover:-translate-y-0.5 hover:shadow-xl">
Export to CSV
</button>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-slate-200 text-left text-sm dark:divide-slate-800">
<tbody id="plReportBody" class="divide-y divide-slate-100 dark:divide-slate-800">
</tbody>
</table>
</div>
</div>
</section>
</main>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>