chore: auto-commit from dashboard
This commit is contained in:
@@ -269,8 +269,8 @@
|
|||||||
"prompt": "Heartbeat check. Rulează src/heartbeat.py printr-un scurt raport de status.\nDacă nu e nimic de raportat (email=0, calendar nu are evenimente <2h, kb ok), răspunde doar cu HEARTBEAT_OK și oprește-te — nu trimite mesaj.\nDacă e ceva: raport scurt pe Discord #echo-work.",
|
"prompt": "Heartbeat check. Rulează src/heartbeat.py printr-un scurt raport de status.\nDacă nu e nimic de raportat (email=0, calendar nu are evenimente <2h, kb ok), răspunde doar cu HEARTBEAT_OK și oprește-te — nu trimite mesaj.\nDacă e ceva: raport scurt pe Discord #echo-work.",
|
||||||
"allowed_tools": [],
|
"allowed_tools": [],
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"last_run": null,
|
"last_run": "2026-04-21T13:00:00.002339+00:00",
|
||||||
"last_status": null,
|
"last_status": "ok",
|
||||||
"next_run": null
|
"next_run": "2026-04-21T15:00:00+00:00"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -43,6 +43,53 @@ from handlers.pdf import PDFHandlers # noqa: E402
|
|||||||
from handlers.workspace import WorkspaceHandlers # noqa: E402
|
from handlers.workspace import WorkspaceHandlers # noqa: E402
|
||||||
from handlers.youtube import YoutubeHandlers # noqa: E402
|
from handlers.youtube import YoutubeHandlers # noqa: E402
|
||||||
|
|
||||||
|
# Shared navigation injected into every served .html via <!--NAV--> marker.
|
||||||
|
NAV_HTML = '''<header class="header">
|
||||||
|
<a href="/echo/index.html" class="logo">
|
||||||
|
<i data-lucide="circle-dot"></i>
|
||||||
|
Echo
|
||||||
|
</a>
|
||||||
|
<nav class="nav">
|
||||||
|
<a href="/echo/index.html" class="nav-item" data-page="index">
|
||||||
|
<i data-lucide="layout-dashboard"></i>
|
||||||
|
<span>Dashboard</span>
|
||||||
|
</a>
|
||||||
|
<a href="/echo/workspace.html" class="nav-item" data-page="workspace">
|
||||||
|
<i data-lucide="code"></i>
|
||||||
|
<span>Workspace</span>
|
||||||
|
</a>
|
||||||
|
<a href="/echo/notes.html" class="nav-item" data-page="notes">
|
||||||
|
<i data-lucide="file-text"></i>
|
||||||
|
<span>KB</span>
|
||||||
|
</a>
|
||||||
|
<a href="/echo/habits.html" class="nav-item" data-page="habits">
|
||||||
|
<i data-lucide="dumbbell"></i>
|
||||||
|
<span>Habits</span>
|
||||||
|
</a>
|
||||||
|
<a href="/echo/files.html" class="nav-item" data-page="files">
|
||||||
|
<i data-lucide="folder"></i>
|
||||||
|
<span>Files</span>
|
||||||
|
</a>
|
||||||
|
<a href="/echo/grup-sprijin.html" class="nav-item" data-page="grup-sprijin">
|
||||||
|
<i data-lucide="heart-handshake"></i>
|
||||||
|
<span>Grup</span>
|
||||||
|
</a>
|
||||||
|
<button class="theme-toggle" onclick="toggleTheme()" title="Schimbă tema">
|
||||||
|
<i data-lucide="sun" id="themeIcon"></i>
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
var path = window.location.pathname;
|
||||||
|
var m = path.match(/([^\\/]+?)(?:\\.html)?$/);
|
||||||
|
var page = m ? m[1] : 'index';
|
||||||
|
if (!page || page === 'echo') page = 'index';
|
||||||
|
var item = document.querySelector('.nav-item[data-page="' + page + '"]');
|
||||||
|
if (item) item.classList.add('active');
|
||||||
|
})();
|
||||||
|
</script>'''
|
||||||
|
|
||||||
|
|
||||||
class TaskBoardHandler(
|
class TaskBoardHandler(
|
||||||
GitHandlers,
|
GitHandlers,
|
||||||
@@ -112,11 +159,28 @@ class TaskBoardHandler(
|
|||||||
self.handle_eco_logs()
|
self.handle_eco_logs()
|
||||||
elif self.path == '/api/eco/doctor':
|
elif self.path == '/api/eco/doctor':
|
||||||
self.handle_eco_doctor()
|
self.handle_eco_doctor()
|
||||||
elif self.path == '/api/eco/git' or self.path.startswith('/api/eco/git?'):
|
|
||||||
self.handle_eco_git_status()
|
|
||||||
elif self.path.startswith('/api/'):
|
elif self.path.startswith('/api/'):
|
||||||
self.send_error(404)
|
self.send_error(404)
|
||||||
else:
|
else:
|
||||||
|
# Inject shared nav into served HTML pages via <!--NAV--> marker.
|
||||||
|
rel = self.path.lstrip('/').split('?')[0]
|
||||||
|
if rel.endswith('.html'):
|
||||||
|
try:
|
||||||
|
fpath = (KANBAN_DIR / rel).resolve()
|
||||||
|
fpath.relative_to(KANBAN_DIR.resolve())
|
||||||
|
except (ValueError, OSError):
|
||||||
|
self.send_error(403)
|
||||||
|
return
|
||||||
|
if fpath.is_file():
|
||||||
|
html = fpath.read_text('utf-8').replace('<!--NAV-->', NAV_HTML)
|
||||||
|
body = html.encode('utf-8')
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header('Content-Type', 'text/html; charset=utf-8')
|
||||||
|
self.send_header('Content-Length', str(len(body)))
|
||||||
|
self.send_header('Cache-Control', 'no-cache')
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(body)
|
||||||
|
return
|
||||||
super().do_GET()
|
super().do_GET()
|
||||||
|
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
|
|||||||
1252
dashboard/eco.html
1252
dashboard/eco.html
File diff suppressed because it is too large
Load Diff
@@ -828,41 +828,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="header">
|
<!--NAV-->
|
||||||
<a href="/echo/index.html" class="logo">
|
|
||||||
<i data-lucide="circle-dot"></i>
|
|
||||||
Echo
|
|
||||||
</a>
|
|
||||||
<nav class="nav">
|
|
||||||
<a href="/echo/index.html" class="nav-item">
|
|
||||||
<i data-lucide="layout-dashboard"></i>
|
|
||||||
<span>Dashboard</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/workspace.html" class="nav-item">
|
|
||||||
<i data-lucide="code"></i>
|
|
||||||
<span>Workspace</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/notes.html" class="nav-item">
|
|
||||||
<i data-lucide="file-text"></i>
|
|
||||||
<span>KB</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/habits.html" class="nav-item">
|
|
||||||
<i data-lucide="dumbbell"></i>
|
|
||||||
<span>Habits</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/files.html" class="nav-item active">
|
|
||||||
<i data-lucide="folder"></i>
|
|
||||||
<span>Files</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/eco.html" class="nav-item">
|
|
||||||
<i data-lucide="cpu"></i>
|
|
||||||
<span>Eco</span>
|
|
||||||
</a>
|
|
||||||
<button class="theme-toggle" onclick="toggleTheme()" title="Schimbă tema">
|
|
||||||
<i data-lucide="sun" id="themeIcon"></i>
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main class="main">
|
<main class="main">
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
|
|||||||
@@ -232,37 +232,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="header">
|
<!--NAV-->
|
||||||
<a href="/echo/index.html" class="logo">
|
|
||||||
<i data-lucide="circle-dot"></i>
|
|
||||||
Echo
|
|
||||||
</a>
|
|
||||||
<nav class="nav">
|
|
||||||
<a href="/echo/index.html" class="nav-item">
|
|
||||||
<i data-lucide="layout-list"></i>
|
|
||||||
<span>Tasks</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/notes.html" class="nav-item">
|
|
||||||
<i data-lucide="file-text"></i>
|
|
||||||
<span>Notes</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/habits.html" class="nav-item">
|
|
||||||
<i data-lucide="dumbbell"></i>
|
|
||||||
<span>Habits</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/files.html" class="nav-item">
|
|
||||||
<i data-lucide="folder"></i>
|
|
||||||
<span>Files</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/grup-sprijin.html" class="nav-item active">
|
|
||||||
<i data-lucide="heart-handshake"></i>
|
|
||||||
<span>Grup</span>
|
|
||||||
</a>
|
|
||||||
<button class="theme-toggle" onclick="toggleTheme()" title="Schimbă tema">
|
|
||||||
<i data-lucide="sun" id="themeIcon"></i>
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main class="main">
|
<main class="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
|
|||||||
@@ -1432,41 +1432,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="header">
|
<!--NAV-->
|
||||||
<a href="/echo/index.html" class="logo">
|
|
||||||
<i data-lucide="circle-dot"></i>
|
|
||||||
Echo
|
|
||||||
</a>
|
|
||||||
<nav class="nav">
|
|
||||||
<a href="/echo/index.html" class="nav-item">
|
|
||||||
<i data-lucide="layout-dashboard"></i>
|
|
||||||
<span>Dashboard</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/workspace.html" class="nav-item">
|
|
||||||
<i data-lucide="code"></i>
|
|
||||||
<span>Workspace</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/notes.html" class="nav-item">
|
|
||||||
<i data-lucide="file-text"></i>
|
|
||||||
<span>KB</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/habits.html" class="nav-item active">
|
|
||||||
<i data-lucide="dumbbell"></i>
|
|
||||||
<span>Habits</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/files.html" class="nav-item">
|
|
||||||
<i data-lucide="folder"></i>
|
|
||||||
<span>Files</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/eco.html" class="nav-item">
|
|
||||||
<i data-lucide="cpu"></i>
|
|
||||||
<span>Eco</span>
|
|
||||||
</a>
|
|
||||||
<button class="theme-toggle" onclick="toggleTheme()" title="Schimbă tema">
|
|
||||||
<i data-lucide="sun" id="themeIcon"></i>
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main class="main">
|
<main class="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"lastUpdated": "2026-03-31T19:39:08.013266",
|
"lastUpdated": "2026-04-21T13:40:29.984484",
|
||||||
"habits": [
|
"habits": [
|
||||||
{
|
{
|
||||||
"id": "95c15eef-3a14-4985-a61e-0b64b72851b0",
|
"id": "95c15eef-3a14-4985-a61e-0b64b72851b0",
|
||||||
@@ -118,6 +118,888 @@
|
|||||||
"createdAt": "2026-02-11T01:58:44.779904",
|
"createdAt": "2026-02-11T01:58:44.779904",
|
||||||
"updatedAt": "2026-02-23T13:08:19.884995",
|
"updatedAt": "2026-02-23T13:08:19.884995",
|
||||||
"lastLivesAward": "2026-02-23"
|
"lastLivesAward": "2026-02-23"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "a34a1d67-64f4-4330-bae1-ecc3abda02fb",
|
||||||
|
"name": "Morning Exercise",
|
||||||
|
"category": "health",
|
||||||
|
"color": "#10b981",
|
||||||
|
"icon": "dumbbell",
|
||||||
|
"priority": 1,
|
||||||
|
"notes": "Start with 10 push-ups",
|
||||||
|
"reminderTime": "07:00",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:39:56.225487",
|
||||||
|
"updatedAt": "2026-04-21T13:39:56.225487"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c38aa0d6-1baf-43a7-8020-aaed45eec38b",
|
||||||
|
"name": "Daily Reading",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:00.242264",
|
||||||
|
"updatedAt": "2026-04-21T13:40:00.242264"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "21dc9ac0-ad31-4bd3-bdd0-eee3a05e9996",
|
||||||
|
"name": "Low Priority",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 10,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:01.101343",
|
||||||
|
"updatedAt": "2026-04-21T13:40:01.101343"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b9c48ef6-88ff-430c-95dc-b26eb41f15a9",
|
||||||
|
"name": "High Priority",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 1,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:01.102455",
|
||||||
|
"updatedAt": "2026-04-21T13:40:01.102455"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "a5410380-d3fa-452c-b0c8-c2aeb3314b7a",
|
||||||
|
"name": "Medium Priority",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:01.103459",
|
||||||
|
"updatedAt": "2026-04-21T13:40:01.103459"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "302ece43-2972-4541-b8d0-6451dce474bf",
|
||||||
|
"name": "Test Habit",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:02.723973",
|
||||||
|
"updatedAt": "2026-04-21T13:40:02.723973"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9ffe612d-2f84-4b75-b589-04457b93109b",
|
||||||
|
"name": "Updated Name",
|
||||||
|
"category": "productivity",
|
||||||
|
"color": "#ef4444",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 1,
|
||||||
|
"notes": "New notes",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:03.528071",
|
||||||
|
"updatedAt": "2026-04-21T13:40:03.529399"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c61e531c-26e0-4223-be38-e3d77e3202d1",
|
||||||
|
"name": "Updated Name",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:04.332914",
|
||||||
|
"updatedAt": "2026-04-21T13:40:04.334508"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "71388af9-fcab-4910-a5d6-74a27cc4676d",
|
||||||
|
"name": "Test Habit",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:05.941683",
|
||||||
|
"updatedAt": "2026-04-21T13:40:05.941683"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edb52e78-e6e0-4a03-973f-0839690bb8ae",
|
||||||
|
"name": "Habit to Delete",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:06.749371",
|
||||||
|
"updatedAt": "2026-04-21T13:40:06.749371"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "595ce224-d2b9-4ce5-b334-2fec32d388ad",
|
||||||
|
"name": "Morning Exercise",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 1,
|
||||||
|
"best": 1,
|
||||||
|
"lastCheckIn": "2026-04-21"
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "check"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"createdAt": "2026-04-21T13:40:09.177939",
|
||||||
|
"updatedAt": "2026-04-21T13:40:09.180079"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "62c9952e-6f6b-47ac-91e3-22cf277767fe",
|
||||||
|
"name": "Meditation",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 1,
|
||||||
|
"best": 1,
|
||||||
|
"lastCheckIn": "2026-04-21"
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "check",
|
||||||
|
"note": "Felt very relaxed today",
|
||||||
|
"rating": 5,
|
||||||
|
"mood": "happy"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"createdAt": "2026-04-21T13:40:09.983840",
|
||||||
|
"updatedAt": "2026-04-21T13:40:09.985357"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f3065043-265b-404f-916e-f662f3d17c66",
|
||||||
|
"name": "Monday Only Habit",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "specific_days",
|
||||||
|
"days": [
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:11.592610",
|
||||||
|
"updatedAt": "2026-04-21T13:40:11.592610"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b97c524e-c243-48da-9d47-4646206806d1",
|
||||||
|
"name": "Water Plants",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 1,
|
||||||
|
"best": 1,
|
||||||
|
"lastCheckIn": "2026-04-21"
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "check"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"createdAt": "2026-04-21T13:40:12.398205",
|
||||||
|
"updatedAt": "2026-04-21T13:40:12.399838"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "21e3c623-3370-4ccc-84a8-e468277ed93a",
|
||||||
|
"name": "Read",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 1,
|
||||||
|
"best": 1,
|
||||||
|
"lastCheckIn": "2026-04-21"
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "check"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"createdAt": "2026-04-21T13:40:13.204268",
|
||||||
|
"updatedAt": "2026-04-21T13:40:13.205755"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c71332bd-c0f2-4e79-bb29-97bb513cc5f1",
|
||||||
|
"name": "Floss",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 1,
|
||||||
|
"best": 1,
|
||||||
|
"lastCheckIn": "2026-04-21"
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "check"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"createdAt": "2026-04-21T13:40:14.009917",
|
||||||
|
"updatedAt": "2026-04-21T13:40:14.011571"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4057bfad-6754-493b-be59-8e6d00a9426b",
|
||||||
|
"name": "Yoga",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 1,
|
||||||
|
"best": 1,
|
||||||
|
"lastCheckIn": "2026-04-21"
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "check"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"createdAt": "2026-04-21T13:40:14.814725",
|
||||||
|
"updatedAt": "2026-04-21T13:40:14.816582"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "135607ac-44c9-4dc5-b8b6-dcb958680e3a",
|
||||||
|
"name": "Journal",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:15.620420",
|
||||||
|
"updatedAt": "2026-04-21T13:40:15.620420"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6166cffa-0bf6-4088-a552-ce8961a6f3d3",
|
||||||
|
"name": "Gratitude",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:16.425718",
|
||||||
|
"updatedAt": "2026-04-21T13:40:16.425718"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f865fb79-e1a9-4eb4-a36d-f1b048db5095",
|
||||||
|
"name": "Daily Exercise",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 2,
|
||||||
|
"completions": [
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "skip"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"createdAt": "2026-04-21T13:40:17.230280",
|
||||||
|
"updatedAt": "2026-04-21T13:40:17.231880"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6926b37a-4b2a-4334-b382-bbacd2f3fa38",
|
||||||
|
"name": "Daily Exercise",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 1,
|
||||||
|
"best": 1,
|
||||||
|
"lastCheckIn": "2026-04-21"
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "check"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"createdAt": "2026-04-21T13:40:18.035252",
|
||||||
|
"updatedAt": "2026-04-21T13:40:18.036876"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "13ca7da8-f9f9-4d04-abe1-72d9e018f729",
|
||||||
|
"name": "Daily Exercise",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 0,
|
||||||
|
"completions": [
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "skip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "skip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "skip"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"createdAt": "2026-04-21T13:40:19.658427",
|
||||||
|
"updatedAt": "2026-04-21T13:40:19.663718"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "100b4b01-10ab-435a-a269-4413fb86e7c8",
|
||||||
|
"name": "Daily Exercise",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 2,
|
||||||
|
"completions": [
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "skip"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"createdAt": "2026-04-21T13:40:20.469061",
|
||||||
|
"updatedAt": "2026-04-21T13:40:20.470855"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cf0cb657-f384-4a0d-a3a5-83bff1a1acaa",
|
||||||
|
"name": "Daily Exercise",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 1,
|
||||||
|
"lastCheckIn": "2026-04-21"
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:21.274674",
|
||||||
|
"updatedAt": "2026-04-21T13:40:21.278631"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1e5ac598-0984-4d5e-b5ff-59d29e95ce7b",
|
||||||
|
"name": "Daily Exercise",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:22.082884",
|
||||||
|
"updatedAt": "2026-04-21T13:40:22.082884"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4ca5ce94-52d8-44ab-9a52-07c4ba736d00",
|
||||||
|
"name": "Daily Exercise",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:23.691843",
|
||||||
|
"updatedAt": "2026-04-21T13:40:23.691843"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "de01102c-e58a-4215-aee3-73211cbca676",
|
||||||
|
"name": "Daily Exercise",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 1,
|
||||||
|
"lastCheckIn": "2026-04-21"
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:24.514309",
|
||||||
|
"updatedAt": "2026-04-21T13:40:24.518328"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6dc26d9f-9631-447e-8669-a36a7b41656a",
|
||||||
|
"name": "Daily Exercise",
|
||||||
|
"category": "other",
|
||||||
|
"color": "#3b82f6",
|
||||||
|
"icon": "check-circle",
|
||||||
|
"priority": 5,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:25.322498",
|
||||||
|
"updatedAt": "2026-04-21T13:40:25.322498"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "58b22147-ea36-4219-ac8a-488df8d7dc0a",
|
||||||
|
"name": "Morning meditation",
|
||||||
|
"category": "health",
|
||||||
|
"color": "#10B981",
|
||||||
|
"icon": "brain",
|
||||||
|
"priority": 50,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 1,
|
||||||
|
"best": 1,
|
||||||
|
"lastCheckIn": "2026-04-21"
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "check"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"createdAt": "2026-04-21T13:40:26.420091",
|
||||||
|
"updatedAt": "2026-04-21T13:40:26.421942"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "00b8e0d2-f146-4446-89c1-42b5efe9b2c6",
|
||||||
|
"name": "Daily exercise",
|
||||||
|
"category": "health",
|
||||||
|
"color": "#EF4444",
|
||||||
|
"icon": "dumbbell",
|
||||||
|
"priority": 50,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:26.925543",
|
||||||
|
"updatedAt": "2026-04-21T13:40:26.925543"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3f97d521-c2eb-4d7d-a5b9-97b99e21fb5a",
|
||||||
|
"name": "Read book",
|
||||||
|
"category": "growth",
|
||||||
|
"color": "#3B82F6",
|
||||||
|
"icon": "book",
|
||||||
|
"priority": 50,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:27.435565",
|
||||||
|
"updatedAt": "2026-04-21T13:40:27.435565"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "24fc969a-6b4c-42ab-822a-d3183d0fc91b",
|
||||||
|
"name": "Yoga practice",
|
||||||
|
"category": "health",
|
||||||
|
"color": "#8B5CF6",
|
||||||
|
"icon": "heart",
|
||||||
|
"priority": 50,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 0,
|
||||||
|
"completions": [
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "skip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "skip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date": "2026-04-21",
|
||||||
|
"type": "skip"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"createdAt": "2026-04-21T13:40:27.944779",
|
||||||
|
"updatedAt": "2026-04-21T13:40:27.949587"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "a3e13785-08c2-4866-a764-45451e8dfbba",
|
||||||
|
"name": "Code review",
|
||||||
|
"category": "work",
|
||||||
|
"color": "#F59E0B",
|
||||||
|
"icon": "code",
|
||||||
|
"priority": 50,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "specific_days",
|
||||||
|
"days": [
|
||||||
|
"monday",
|
||||||
|
"wednesday"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:28.453967",
|
||||||
|
"updatedAt": "2026-04-21T13:40:28.457179"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "18eddd3f-c4f0-4023-ac68-6e3cd33ca04a",
|
||||||
|
"name": "Guitar practice",
|
||||||
|
"category": "personal",
|
||||||
|
"color": "#EC4899",
|
||||||
|
"icon": "music",
|
||||||
|
"priority": 50,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:28.962515",
|
||||||
|
"updatedAt": "2026-04-21T13:40:28.962515"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8509c820-c187-4d7d-9f01-d754c7ff6bc3",
|
||||||
|
"name": "Gym workout",
|
||||||
|
"category": "health",
|
||||||
|
"color": "#EF4444",
|
||||||
|
"icon": "dumbbell",
|
||||||
|
"priority": 50,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "specific_days",
|
||||||
|
"days": [
|
||||||
|
"monday",
|
||||||
|
"wednesday"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:29.478868",
|
||||||
|
"updatedAt": "2026-04-21T13:40:29.478868"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e0961521-81f7-471f-b794-c42d2650a95b",
|
||||||
|
"name": "Meditation",
|
||||||
|
"category": "health",
|
||||||
|
"color": "#10B981",
|
||||||
|
"icon": "brain",
|
||||||
|
"priority": 50,
|
||||||
|
"notes": "",
|
||||||
|
"reminderTime": "",
|
||||||
|
"frequency": {
|
||||||
|
"type": "daily"
|
||||||
|
},
|
||||||
|
"streak": {
|
||||||
|
"current": 0,
|
||||||
|
"best": 0,
|
||||||
|
"lastCheckIn": null
|
||||||
|
},
|
||||||
|
"lives": 3,
|
||||||
|
"completions": [],
|
||||||
|
"createdAt": "2026-04-21T13:40:29.984484",
|
||||||
|
"updatedAt": "2026-04-21T13:40:29.984484"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -102,41 +102,6 @@ class GitHandlers:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.send_json({'error': str(e)}, 500)
|
self.send_json({'error': str(e)}, 500)
|
||||||
|
|
||||||
# ── /api/eco/git (echo-core repo) ────────────────────────────
|
|
||||||
def handle_eco_git_status(self):
|
|
||||||
"""Get git status for echo-core repo."""
|
|
||||||
try:
|
|
||||||
workspace = constants.ECHO_CORE_DIR
|
|
||||||
|
|
||||||
branch = self._run_git(workspace, ['branch', '--show-current']).stdout.strip()
|
|
||||||
last_commit = self._run_git(workspace, ['log', '-1', '--format=%h|%s|%cr']).stdout.strip()
|
|
||||||
commit_parts = last_commit.split('|') if last_commit else ['', '', '']
|
|
||||||
|
|
||||||
status_output = self._run_git(workspace, ['status', '--short']).stdout.strip()
|
|
||||||
uncommitted = [f for f in status_output.split('\n') if f.strip()] if status_output else []
|
|
||||||
|
|
||||||
uncommitted_parsed = []
|
|
||||||
for line in uncommitted:
|
|
||||||
if len(line) >= 2:
|
|
||||||
status = line[:2].strip()
|
|
||||||
filepath = line[2:].strip()
|
|
||||||
if filepath:
|
|
||||||
uncommitted_parsed.append({'status': status, 'path': filepath})
|
|
||||||
|
|
||||||
self.send_json({
|
|
||||||
'branch': branch,
|
|
||||||
'clean': len(uncommitted) == 0,
|
|
||||||
'uncommittedCount': len(uncommitted),
|
|
||||||
'uncommittedParsed': uncommitted_parsed,
|
|
||||||
'lastCommit': {
|
|
||||||
'hash': commit_parts[0] if len(commit_parts) > 0 else '',
|
|
||||||
'message': commit_parts[1] if len(commit_parts) > 1 else '',
|
|
||||||
'time': commit_parts[2] if len(commit_parts) > 2 else '',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
except Exception as e:
|
|
||||||
self.send_json({'error': str(e)}, 500)
|
|
||||||
|
|
||||||
def handle_eco_git_commit(self):
|
def handle_eco_git_commit(self):
|
||||||
"""Run git add, commit, and push for echo-core repo."""
|
"""Run git add, commit, and push for echo-core repo."""
|
||||||
try:
|
try:
|
||||||
|
|||||||
1253
dashboard/index.html
1253
dashboard/index.html
File diff suppressed because it is too large
Load Diff
@@ -678,41 +678,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="header">
|
<!--NAV-->
|
||||||
<a href="/echo/index.html" class="logo">
|
|
||||||
<i data-lucide="circle-dot"></i>
|
|
||||||
Echo
|
|
||||||
</a>
|
|
||||||
<nav class="nav">
|
|
||||||
<a href="/echo/index.html" class="nav-item">
|
|
||||||
<i data-lucide="layout-dashboard"></i>
|
|
||||||
<span>Dashboard</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/workspace.html" class="nav-item">
|
|
||||||
<i data-lucide="code"></i>
|
|
||||||
<span>Workspace</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/notes.html" class="nav-item active">
|
|
||||||
<i data-lucide="file-text"></i>
|
|
||||||
<span>KB</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/habits.html" class="nav-item">
|
|
||||||
<i data-lucide="dumbbell"></i>
|
|
||||||
<span>Habits</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/files.html" class="nav-item">
|
|
||||||
<i data-lucide="folder"></i>
|
|
||||||
<span>Files</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/eco.html" class="nav-item">
|
|
||||||
<i data-lucide="cpu"></i>
|
|
||||||
<span>Eco</span>
|
|
||||||
</a>
|
|
||||||
<button class="theme-toggle" onclick="toggleTheme()" title="Schimbă tema">
|
|
||||||
<i data-lucide="sun" id="themeIcon"></i>
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main class="main">
|
<main class="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Swipe left/right to navigate between pages
|
* Swipe left/right to navigate between pages
|
||||||
*/
|
*/
|
||||||
(function() {
|
(function() {
|
||||||
const pages = ['index.html', 'eco.html', 'notes.html', 'habits.html', 'files.html', 'workspace.html'];
|
const pages = ['index.html', 'notes.html', 'habits.html', 'files.html', 'workspace.html'];
|
||||||
|
|
||||||
// Get current page index
|
// Get current page index
|
||||||
function getCurrentIndex() {
|
function getCurrentIndex() {
|
||||||
|
|||||||
@@ -422,41 +422,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="header">
|
<!--NAV-->
|
||||||
<a href="/echo/index.html" class="logo">
|
|
||||||
<i data-lucide="circle-dot"></i>
|
|
||||||
Echo
|
|
||||||
</a>
|
|
||||||
<nav class="nav">
|
|
||||||
<a href="/echo/index.html" class="nav-item">
|
|
||||||
<i data-lucide="layout-dashboard"></i>
|
|
||||||
<span>Dashboard</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/workspace.html" class="nav-item active">
|
|
||||||
<i data-lucide="code"></i>
|
|
||||||
<span>Workspace</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/notes.html" class="nav-item">
|
|
||||||
<i data-lucide="file-text"></i>
|
|
||||||
<span>KB</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/habits.html" class="nav-item">
|
|
||||||
<i data-lucide="dumbbell"></i>
|
|
||||||
<span>Habits</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/files.html" class="nav-item">
|
|
||||||
<i data-lucide="folder"></i>
|
|
||||||
<span>Files</span>
|
|
||||||
</a>
|
|
||||||
<a href="/echo/eco.html" class="nav-item">
|
|
||||||
<i data-lucide="cpu"></i>
|
|
||||||
<span>Eco</span>
|
|
||||||
</a>
|
|
||||||
<button class="theme-toggle" onclick="toggleTheme()" title="Schimba tema">
|
|
||||||
<i data-lucide="sun" id="themeIcon"></i>
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main class="main">
|
<main class="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
|
|||||||
88
tests/test_dashboard_unified_index.py
Normal file
88
tests/test_dashboard_unified_index.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
"""Smoke tests for the unified Echo dashboard (post index+eco merge)."""
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import requests
|
||||||
|
|
||||||
|
BASE = 'http://localhost:8088'
|
||||||
|
DASH = Path(__file__).resolve().parent.parent / 'dashboard'
|
||||||
|
|
||||||
|
SIBLING_PAGES = ['index', 'habits', 'files', 'workspace', 'notes', 'grup-sprijin']
|
||||||
|
|
||||||
|
|
||||||
|
def _taskboard_running():
|
||||||
|
try:
|
||||||
|
return requests.get(f'{BASE}/api/status', timeout=2).ok
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.skipif(
|
||||||
|
not _taskboard_running(),
|
||||||
|
reason='echo-taskboard not reachable on :8088 — start it with `systemctl --user start echo-taskboard`',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('page', SIBLING_PAGES)
|
||||||
|
def test_no_eco_link_on_sibling_pages(page):
|
||||||
|
"""No sibling page should still have a clickable link to the removed eco.html."""
|
||||||
|
r = requests.get(f'{BASE}/{page}.html', timeout=5)
|
||||||
|
assert r.status_code == 200, f'{page}.html returned {r.status_code}'
|
||||||
|
# Only reject actual hyperlinks, not historical code comments.
|
||||||
|
assert 'href="/echo/eco.html"' not in r.text, f'{page}.html still links to eco.html'
|
||||||
|
assert "href='/echo/eco.html'" not in r.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_index_has_all_panels():
|
||||||
|
"""Unified index must render Git, Services, Sessions, Logs, Doctor panels."""
|
||||||
|
r = requests.get(f'{BASE}/index.html', timeout=5)
|
||||||
|
assert r.status_code == 200
|
||||||
|
for sid in ['sec-git', 'sec-services', 'sec-sessions', 'sec-logs', 'sec-doctor']:
|
||||||
|
assert f'id="{sid}"' in r.text, f'missing panel #{sid}'
|
||||||
|
|
||||||
|
|
||||||
|
def test_swipe_nav_no_eco():
|
||||||
|
"""swipe-nav.js hardcoded page list must no longer include eco.html."""
|
||||||
|
js = (DASH / 'swipe-nav.js').read_text(encoding='utf-8')
|
||||||
|
assert "'eco.html'" not in js
|
||||||
|
|
||||||
|
|
||||||
|
def test_eco_html_gone():
|
||||||
|
"""The old eco.html route should now 404."""
|
||||||
|
r = requests.get(f'{BASE}/eco.html', timeout=5)
|
||||||
|
assert r.status_code == 404
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_git_has_diffstat_field():
|
||||||
|
"""Regression guard: the Git panel depends on /api/git returning diffStat."""
|
||||||
|
r = requests.get(f'{BASE}/api/git', timeout=5)
|
||||||
|
assert r.status_code == 200
|
||||||
|
data = r.json()
|
||||||
|
assert 'diffStat' in data
|
||||||
|
assert 'uncommittedParsed' in data
|
||||||
|
assert 'lastCommit' in data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_eco_git_removed():
|
||||||
|
"""The orphaned /api/eco/git GET handler was deleted during unification."""
|
||||||
|
r = requests.get(f'{BASE}/api/eco/git', timeout=5)
|
||||||
|
assert r.status_code == 404
|
||||||
|
|
||||||
|
|
||||||
|
def test_nav_injected_server_side():
|
||||||
|
"""Server must replace <!--NAV--> with real nav markup on every .html."""
|
||||||
|
r = requests.get(f'{BASE}/index.html', timeout=5)
|
||||||
|
assert r.status_code == 200
|
||||||
|
assert '<!--NAV-->' not in r.text, 'server did not expand the NAV marker'
|
||||||
|
assert 'class="nav-item"' in r.text, 'nav did not render'
|
||||||
|
assert 'data-page="index"' in r.text
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('page', SIBLING_PAGES)
|
||||||
|
def test_nav_present_on_all_pages(page):
|
||||||
|
"""Every sibling page must receive the injected nav."""
|
||||||
|
r = requests.get(f'{BASE}/{page}.html', timeout=5)
|
||||||
|
assert r.status_code == 200
|
||||||
|
assert 'class="nav-item"' in r.text
|
||||||
|
assert '<!--NAV-->' not in r.text
|
||||||
Reference in New Issue
Block a user