Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
edd5e12
fix: add loading state and disabled guard to Google OAuth buttons on …
hafiz-ahtasham-ali Feb 23, 2026
d67d616
feat(dashboard): build habit dashboard UI with donut chart, heatmap, …
hafiz-ahtasham-ali Feb 23, 2026
f071422
feat(dashboard): add AI processing pipeline section with animated 3-s…
hafiz-ahtasham-ali Feb 23, 2026
43d43e3
Fix division by zero in HabitHeatmap average calculation when no acti…
hafiz-ahtasham-ali Feb 24, 2026
901259b
Fix lint errors: refactor DonutChart variable reassignment, suppress …
hafiz-ahtasham-ali Feb 24, 2026
50080dd
Rename "Avg score" and "Best day" labels to "Avg." and "Best" in habi…
hafiz-ahtasham-ali Feb 24, 2026
4083119
Polish monthly habit heatmap with responsive scaling, streak emphasis…
hafiz-ahtasham-ali Feb 24, 2026
58a5294
feat(dashboard): replace structured JSON output with animated live da…
hafiz-ahtasham-ali Feb 24, 2026
1e44d43
feat(features): move AI pipeline demo to features page and remove it …
hafiz-ahtasham-ali Feb 24, 2026
7f12116
Improve dashboard-to-logs navigation with contextual links, breadcrum…
hafiz-ahtasham-ali Feb 24, 2026
ba968f0
Polish nav UI: better arrow on View All Logs, remove redundant Back t…
hafiz-ahtasham-ali Feb 24, 2026
b14b1a0
Comment out Goals sort toggle and Needs attention summary
hafiz-ahtasham-ali Feb 24, 2026
122e686
Remove Duration label from AI Extraction concept tags
hafiz-ahtasham-ali Feb 24, 2026
c9740dc
fix: resolve eslint errors and warnings in dashboard and logs components
hafiz-ahtasham-ali Feb 24, 2026
8b7ecc3
Wrap logs page in Suspense for useSearchParams
hafiz-ahtasham-ali Feb 25, 2026
ad3aad6
Layout: make Month Snapshot card fill horizontal space and match cale…
hafiz-ahtasham-ali Feb 25, 2026
3d842e2
Add /stats page with mock analytics, charts, and event inspect modal
hafiz-ahtasham-ali Feb 25, 2026
02e0e27
Add dual dashboard modes: new compact view and legacy classic view
hafiz-ahtasham-ali Feb 25, 2026
a9f7251
Remove logs page breadcrumb to match statistics navigation
hafiz-ahtasham-ali Feb 25, 2026
64f5364
Remove Account Settings breadcrumb for sidebar-only dashboard navigation
hafiz-ahtasham-ali Feb 25, 2026
2397d72
fix: align dashboard top spacing with logs, stats, and account pages
hafiz-ahtasham-ali Feb 25, 2026
dfb83a5
Add Support & Feedback form with Resend integration and premium modal UI
hafiz-ahtasham-ali Feb 25, 2026
388343c
fix: resolve ESLint issues (useEffect deps, prefer-const)
hafiz-ahtasham-ali Feb 25, 2026
e33b383
fix: point next-env.d.ts at .next/dev/types/routes.d.ts for Next 16
hafiz-ahtasham-ali Feb 26, 2026
c37a097
feat: drive logs, dashboard, and stats from generated test-logs data
hafiz-ahtasham-ali Feb 26, 2026
092e0f4
fix(LogsPage): resolve lint by deriving URL/view/date in render and k…
hafiz-ahtasham-ali Feb 26, 2026
23fc58b
Add file attachment to Help & Feedback form
hafiz-ahtasham-ali Feb 26, 2026
63ee1f6
Improve Help & Feedback modal UI: layout, inputs, dropzone, and polish
hafiz-ahtasham-ali Feb 26, 2026
048aefe
fix: Today's Overview always uses today's date and shows empty state …
hafiz-ahtasham-ali Feb 27, 2026
85b867d
Logs Explorer: scrollable panel with dark-themed scrollbar and pagina…
hafiz-ahtasham-ali Feb 27, 2026
5d212ba
Add goals pages and integrate goals navigation into dashboard
hafiz-ahtasham-ali Feb 27, 2026
31724ea
feat: align goals pages UI with dashboard non-compact/logs/stats style
hafiz-ahtasham-ali Feb 27, 2026
2f448f0
add optional goal metadata to generated test logs for goals pages
hafiz-ahtasham-ali Feb 27, 2026
4a3d300
replace hardcoded goals pages data with test-logs derived goals data
hafiz-ahtasham-ali Feb 27, 2026
e9676a2
Add reusable goals radar chart with test-log-derived data and inferre…
hafiz-ahtasham-ali Feb 27, 2026
c9070a3
Add themed Logs Explorer-style scroll container to goal detail Timeline
hafiz-ahtasham-ali Feb 27, 2026
462fcc8
feat(goals): add log-driven Goal Insights with progress, milestones, …
hafiz-ahtasham-ali Feb 27, 2026
23942a1
add taxonomy-based goal/timeline tags with local persistence and fix …
hafiz-ahtasham-ali Feb 27, 2026
58b7b9d
fix: remove unused dashboard imports and align goal tag color typing
hafiz-ahtasham-ali Feb 27, 2026
92802e1
fix: lazily initialize Resend in support API to prevent CI build fail…
hafiz-ahtasham-ali Feb 27, 2026
530c5b8
Hide dashboard controls until WhatsApp is connected
hafiz-ahtasham-ali Mar 2, 2026
a0e303a
replace dashboard mock fallback data with true empty states
hafiz-ahtasham-ali Mar 2, 2026
32d9017
remove mock logs fallback and show empty state on logs page
hafiz-ahtasham-ali Mar 2, 2026
b8d6765
switch goals page to log-derived data with clean empty states
hafiz-ahtasham-ali Mar 2, 2026
31f70dc
migrate goal detail page to test-log-derived data source
hafiz-ahtasham-ali Mar 2, 2026
c830570
remove stats mock fallback and add real empty-state handling
hafiz-ahtasham-ali Mar 2, 2026
1ac67f0
add global demo data mode toggle for dashboard logs goals and stats
hafiz-ahtasham-ali Mar 2, 2026
a9f4741
ignore and untrack website test-logs data file
hafiz-ahtasham-ali Mar 2, 2026
7906a9b
Refactor goal detail state to derive source goals and remove effect-d…
hafiz-ahtasham-ali Mar 2, 2026
2d004bb
fix: track website/data/test-logs.json to resolve TypeScript module i…
hafiz-ahtasham-ali Mar 2, 2026
21f0c97
Add end-to-end Telegram linking via deep-link token resolution with O…
hafiz-ahtasham-ali Mar 3, 2026
1c6e2e0
Polish Telegram integration UX by enabling widget/CTA links
hafiz-ahtasham-ali Mar 3, 2026
1bcc70d
chore: remove unused RadarMini chart component
hafiz-ahtasham-ali Mar 4, 2026
a7f9e8a
chore: remove unused mock data files from website/data
hafiz-ahtasham-ali Mar 4, 2026
985b089
chore: remove unused Navbar component
hafiz-ahtasham-ali Mar 4, 2026
6a1b8fe
consolidate components from app/components/ into components/
hafiz-ahtasham-ali Mar 4, 2026
cea5892
remove empty components/layout and components/ui directories
hafiz-ahtasham-ali Mar 4, 2026
297afd5
move GoalCard and GoalRadarPanel into components/goals/
hafiz-ahtasham-ali Mar 4, 2026
c67cb8f
move hero and AnimatedComparison into components/hero and components/…
hafiz-ahtasham-ali Mar 4, 2026
969ffbd
move contexts/ out of app/ into website/contexts/
hafiz-ahtasham-ali Mar 4, 2026
b95b8b9
Remove unused GoalListItem type import from goals page
hafiz-ahtasham-ali Mar 4, 2026
afc133f
Remove Quick Actions from dashboard and make WhatsApp Session full width
hafiz-ahtasham-ali Mar 5, 2026
817219f
gate advanced account pages behind developer settings toggle
hafiz-ahtasham-ali Mar 9, 2026
7e42ed8
chore: remove unused pollNow value while preserving dashboard polling…
hafiz-ahtasham-ali Mar 9, 2026
d289ca0
fix: restore dashboard countdownSeconds calculation for successful pr…
hafiz-ahtasham-ali Mar 9, 2026
d1af362
Fix malformed plugin route handlers causing TypeScript parse errors
hafiz-ahtasham-ali Mar 9, 2026
7e8489c
fix: allow WhatsApp verification when developer settings are disabled…
hafiz-ahtasham-ali Mar 10, 2026
920b405
add full user unregister cleanup for agents, workspaces, and peer ass…
hafiz-ahtasham-ali Mar 10, 2026
884dd8e
Show Today/Monthly overview plus dashboard header controls when welco…
hafiz-ahtasham-ali Mar 10, 2026
100482b
Isolate non-developer dashboard overview sections from developer mode
hafiz-ahtasham-ali Mar 11, 2026
8d651b5
Preserve existing AGENTS.md instructions and append per-user inbound …
hafiz-ahtasham-ali Mar 11, 2026
a098d69
Require AGENTS audio metadata rule to derive duration_seconds from ac…
hafiz-ahtasham-ali Mar 12, 2026
9ea273d
Add per-user audio-metadata AGENTS rule and /loglife/audio-metadata A…
hafiz-ahtasham-ali Mar 12, 2026
451343c
Make non-developer Monthly Habit Overview dynamic from current-month …
hafiz-ahtasham-ali Mar 12, 2026
41e8f9c
fix some plugin issues after updating openclaw
hafiz-ahtasham-ali Mar 25, 2026
51d95df
fix audio data on dashboard
hafiz-ahtasham-ali Mar 26, 2026
959563e
fix(loglife): preserve/bootstrap main agent and clear stale peer rout…
hafiz-ahtasham-ali Mar 27, 2026
ef53b2e
fix(loglife): sync per-user workspace templates on register and re-re…
hafiz-ahtasham-ali Mar 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 93 additions & 8 deletions plugin/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ describe("POST /loglife/register handler", () => {
let mockWriteFileSync: ReturnType<typeof vi.fn>;
let mockUtimesSync: ReturnType<typeof vi.fn>;
let usersJsonContent: string;
let openclawJsonContent: string;

beforeEach(async () => {
vi.resetModules();
Expand All @@ -533,12 +534,23 @@ describe("POST /loglife/register handler", () => {
users: [],
defaults: { dmScope: "main" },
});
openclawJsonContent = JSON.stringify({
agents: { list: [{ id: "main", name: "Main Agent" }] },
bindings: [],
channels: { whatsapp: { groupPolicy: "allowlist" } },
session: {},
});

mockWriteFileSync = vi.fn();
mockUtimesSync = vi.fn();

vi.doMock("node:fs", () => ({
readFileSync: vi.fn().mockImplementation(() => usersJsonContent),
readFileSync: vi.fn().mockImplementation((path: string) => {
if (path.includes("users.json")) return usersJsonContent;
if (path.includes("peer-agent-assignments.json")) return "{}";
if (path.includes("openclaw.json")) return openclawJsonContent;
return "# AGENTS.md\n";
}),
writeFileSync: mockWriteFileSync,
mkdirSync: vi.fn(),
existsSync: vi.fn().mockReturnValue(true),
Expand Down Expand Up @@ -624,10 +636,49 @@ describe("POST /loglife/register handler", () => {
expect(body.userId).toBeDefined();
expect(body.linkCode).toMatch(/^LF-\d{4}$/);

// users.json, generated.json, openclaw.json, pending-links.json
expect(mockWriteFileSync).toHaveBeenCalledTimes(4);
// users.json, generated.json, openclaw.json, pending-links.json,
// plus peer-agent-assignments.json cleanup for stale sticky routes
expect(mockWriteFileSync).toHaveBeenCalledTimes(5);
// utimesSync no longer used — openclaw.json is written directly
expect(mockUtimesSync).not.toHaveBeenCalled();

const openclawWrite = mockWriteFileSync.mock.calls.find(([path]) =>
String(path).includes("openclaw.json"),
);
expect(openclawWrite).toBeDefined();
const writtenOpenclaw = JSON.parse(String(openclawWrite![1])) as {
agents?: { list?: Array<{ id: string }> };
};
expect(writtenOpenclaw.agents?.list?.map((a) => a.id)).toEqual(["main", "alice"]);
});

it("bootstraps explicit main agent when openclaw.json has none", async () => {
openclawJsonContent = JSON.stringify({
agents: { defaults: { model: { primary: "openai/gpt-4o-mini" } } },
bindings: [],
channels: { whatsapp: { groupPolicy: "allowlist" } },
session: {},
});

const req = mockReq({
method: "POST",
url: "/loglife/register",
headers: { authorization: `Bearer ${API_KEY}` },
body: { phone: "+15551234567", name: "Alice" },
});
const res = mockRes();
await registerHandler(req, res);

expect(res._status).toBe(200);
const openclawWrite = mockWriteFileSync.mock.calls.find(([path]) =>
String(path).includes("openclaw.json"),
);
expect(openclawWrite).toBeDefined();
const writtenOpenclaw = JSON.parse(String(openclawWrite![1])) as {
agents?: { list?: Array<{ id: string; name?: string }> };
};
expect(writtenOpenclaw.agents?.list?.map((a) => a.id)).toEqual(["main", "alice"]);
expect(writtenOpenclaw.agents?.list?.[0]?.name).toBe("Main");
});

it("uses name to derive user ID", async () => {
Expand Down Expand Up @@ -664,8 +715,8 @@ describe("POST /loglife/register handler", () => {
expect(body.existing).toBe(true);
expect(body.linkCode).toMatch(/^LF-\d{4}$/);

// Existing user still gets a fresh pending-link entry on disk
expect(mockWriteFileSync).toHaveBeenCalledTimes(1);
// Existing user refreshes config, syncs workspace AGENTS, and writes pending-link entry.
expect(mockWriteFileSync).toHaveBeenCalledTimes(4);
expect(mockWriteFileSync).toHaveBeenCalledWith(
expect.stringContaining("pending-links.json"),
expect.any(String),
Expand All @@ -682,6 +733,7 @@ describe("POST /loglife/unregister handler", () => {

let unregisterHandler: RouteHandler;
let mockWriteFileSync: ReturnType<typeof vi.fn>;
let mockRmSync: ReturnType<typeof vi.fn>;
let usersJsonContent: string;

beforeEach(async () => {
Expand All @@ -696,17 +748,25 @@ describe("POST /loglife/unregister handler", () => {
});

mockWriteFileSync = vi.fn();
mockRmSync = vi.fn();

vi.doMock("node:fs", () => ({
readFileSync: vi.fn().mockImplementation((path: string) => {
if (path.includes("users.json")) return usersJsonContent;
if (path.includes("peer-agent-assignments.json")) {
return JSON.stringify({
"whatsapp:default:dm:+15551234567": "alice",
"whatsapp:default:dm:+19999999999": "main",
});
}
return JSON.stringify({
agents: { defaults: { model: { primary: "x" } } },
channels: { whatsapp: { groupPolicy: "allowlist", debounceMs: 0, mediaMaxMb: 50 } },
bindings: [],
});
}),
writeFileSync: mockWriteFileSync,
rmSync: mockRmSync,
mkdirSync: vi.fn(),
existsSync: vi.fn().mockReturnValue(true),
}));
Expand Down Expand Up @@ -774,6 +834,7 @@ describe("POST /loglife/unregister handler", () => {
expect(res._status).toBe(200);
expect(res.json()).toEqual({ removed: false, existing: false });
expect(mockWriteFileSync).not.toHaveBeenCalled();
expect(mockRmSync).not.toHaveBeenCalled();
});

it("unregisters matching phone and rewrites config files", async () => {
Expand All @@ -791,8 +852,16 @@ describe("POST /loglife/unregister handler", () => {
expect(body.removed).toBe(true);
expect(body.removedUserIds).toEqual(["alice"]);

// users.json, generated.json, openclaw.json
expect(mockWriteFileSync).toHaveBeenCalledTimes(3);
// users.json, generated.json, openclaw.json, peer-agent-assignments.json
expect(mockWriteFileSync).toHaveBeenCalledTimes(4);
expect(mockRmSync).toHaveBeenCalledWith(
expect.stringContaining("/agents/alice"),
{ recursive: true, force: true },
);
expect(mockRmSync).toHaveBeenCalledWith(
expect.stringContaining("/workspace-alice"),
{ recursive: true, force: true },
);
});

it("supports all:true to clear all users", async () => {
Expand All @@ -810,7 +879,23 @@ describe("POST /loglife/unregister handler", () => {
expect(body.removedAll).toBe(true);
expect(body.removed).toBe(true);
expect(body.removedUserIds).toEqual(["alice", "bob"]);
expect(mockWriteFileSync).toHaveBeenCalledTimes(4);
expect(mockWriteFileSync).toHaveBeenCalledTimes(5);
expect(mockRmSync).toHaveBeenCalledWith(
expect.stringContaining("/agents/alice"),
{ recursive: true, force: true },
);
expect(mockRmSync).toHaveBeenCalledWith(
expect.stringContaining("/workspace-alice"),
{ recursive: true, force: true },
);
expect(mockRmSync).toHaveBeenCalledWith(
expect.stringContaining("/agents/bob"),
{ recursive: true, force: true },
);
expect(mockRmSync).toHaveBeenCalledWith(
expect.stringContaining("/workspace-bob"),
{ recursive: true, force: true },
);
});
});

Expand Down
Loading
Loading