From 3ba71500bc2d60a00ca81b9439305029670f4d52 Mon Sep 17 00:00:00 2001 From: Adam Mathes Date: Fri, 13 Feb 2026 06:55:21 -0800 Subject: Implement Frontend Feed Items View with tests --- .thicket/tickets.jsonl | 4 +- frontend/coverage/clover.xml | 66 +++-- frontend/coverage/coverage-final.json | 6 +- frontend/coverage/index.html | 38 +-- frontend/coverage/src/App.css.html | 2 +- frontend/coverage/src/App.tsx.html | 18 +- .../coverage/src/components/FeedItems.css.html | 262 +++++++++++++++++++ .../coverage/src/components/FeedItems.tsx.html | 283 +++++++++++++++++++++ frontend/coverage/src/components/FeedList.css.html | 2 +- frontend/coverage/src/components/FeedList.tsx.html | 25 +- frontend/coverage/src/components/Login.css.html | 2 +- frontend/coverage/src/components/Login.tsx.html | 2 +- frontend/coverage/src/components/index.html | 48 +++- frontend/coverage/src/index.html | 2 +- frontend/src/App.tsx | 6 +- frontend/src/components/FeedItems.css | 60 +++++ frontend/src/components/FeedItems.test.tsx | 52 ++++ frontend/src/components/FeedItems.tsx | 66 +++++ frontend/src/components/FeedList.test.tsx | 26 +- frontend/src/components/FeedList.tsx | 5 +- frontend/src/types.ts | 14 + 21 files changed, 912 insertions(+), 77 deletions(-) create mode 100644 frontend/coverage/src/components/FeedItems.css.html create mode 100644 frontend/coverage/src/components/FeedItems.tsx.html create mode 100644 frontend/src/components/FeedItems.css create mode 100644 frontend/src/components/FeedItems.test.tsx create mode 100644 frontend/src/components/FeedItems.tsx diff --git a/.thicket/tickets.jsonl b/.thicket/tickets.jsonl index e3174e1..8af7e2b 100644 --- a/.thicket/tickets.jsonl +++ b/.thicket/tickets.jsonl @@ -2,12 +2,13 @@ {"id":"NK-0ppv3f","title":"Implement Frontend Settings","description":"Create settings page for managing feeds/categories.","type":"task","status":"open","priority":2,"labels":null,"assignee":"","created":"2026-02-13T05:44:01.631640578Z","updated":"2026-02-13T05:44:01.631640578Z"} {"id":"NK-1phdpf","title":"refactor backend to have a clean API","description":"create a nice clean API for the backend GO code that is more independent of the frontend\n\nensure that it is working with good tests","type":"task","status":"closed","priority":1,"labels":null,"assignee":"","created":"2026-02-13T01:52:49.8322638Z","updated":"2026-02-13T04:26:47.517515371Z"} {"id":"NK-27or4b","title":"Increase Test Coverage to \u003e80%","description":"Project-wide test coverage is currently ~63%. Key gaps are in the new and packages, as well as some core model logic. Increase coverage to at least 80% to ensure stability.","type":"task","status":"open","priority":3,"labels":null,"assignee":"","created":"2026-02-13T05:03:09.677147894Z","updated":"2026-02-13T05:03:09.677147894Z"} -{"id":"NK-3om7x2","title":"Implement Feed Items View","description":"Create a component to display items for a selected feed. Fetch items from /api/stream?feed_id=...","type":"","status":"open","priority":1,"labels":null,"assignee":"","created":"2026-02-13T05:59:46.161356437Z","updated":"2026-02-13T05:59:46.161356437Z"} +{"id":"NK-3om7x2","title":"Implement Feed Items View","description":"Create a component to display items for a selected feed. Fetch items from /api/stream?feed_id=...","type":"","status":"closed","priority":1,"labels":null,"assignee":"","created":"2026-02-13T05:59:46.161356437Z","updated":"2026-02-13T14:55:14.795643835Z"} {"id":"NK-6q9nyg","title":"Refactor HTTP-dependent functions for testability","description":"Several functions use http.Get or external libraries directly (GetFullContent uses goose, ResolveFeedURL uses http.Get + goquery, imageProxyHandler uses http.Client). Refactor these to accept interfaces for HTTP fetching so they can be unit tested with mocks. This is the primary blocker for reaching 90% coverage.","type":"cleanup","status":"open","priority":3,"labels":null,"assignee":"","created":"2026-02-13T03:54:37.630148644Z","updated":"2026-02-13T03:54:37.630148644Z"} {"id":"NK-7tzbql","title":"Fix TUI Content View Navigation and Interaction","description":"The TUI content view (reading a single item) is currently non-functional or severely limited. Users cannot easily navigate back, scroll, or interact with the content. This task involves improving the 'viewContent' state in the TUI.","type":"bug","status":"closed","priority":1,"labels":null,"assignee":"","created":"2026-02-13T05:02:57.382793121Z","updated":"2026-02-13T05:06:15.144485446Z"} {"id":"NK-9hx0y7","title":"Implement Frontend Login","description":"Create login page and auth logic in the new React frontend. Port functionality from legacy login.html.","type":"","status":"closed","priority":1,"labels":null,"assignee":"","created":"2026-02-13T05:44:01.546395342Z","updated":"2026-02-13T05:50:33.877452063Z"} {"id":"NK-bsdwqz","title":"terminal UI","description":"once there is good test coverage and a clean backend API, work on a nice efficient TUI with https://github.com/charmbracelet/bubbletea","type":"task","status":"closed","priority":1,"labels":null,"assignee":"","created":"2026-02-13T01:54:02.285738454Z","updated":"2026-02-13T04:42:09.824268427Z"} {"id":"NK-ed1iah","title":"Make feed crawling async in API","description":"Currently, POST /api/feed triggers an immediate crawl which blocks the response (or at least keeps the goroutine alive). Refactor the crawling architecture to be truly async with a job queue or status updates, improving API responsiveness and reliability.","type":"cleanup","status":"open","priority":3,"labels":null,"assignee":"","created":"2026-02-13T04:26:55.908243985Z","updated":"2026-02-13T04:26:55.908243985Z"} +{"id":"NK-ek0cox","title":"Implement Item Interactions","description":"Add ability to toggle read/unread and star/unstar status for items. Use PUT /item/:id","type":"","status":"open","priority":1,"labels":null,"assignee":"","created":"2026-02-13T14:55:14.825454967Z","updated":"2026-02-13T14:55:14.825454967Z"} {"id":"NK-gqkh96","title":"Remaining test coverage gaps","description":"Cross-package test coverage is at 81.2%. The remaining untested functions are: GetFullContent (goose HTTP extraction), indexHandler/serveBoxedFile (rice.MustFindBox), Serve (starts HTTP server), main, util.init. To reach 90%, consider: (1) refactoring GetFullContent to accept an interface for HTTP fetching, (2) refactoring Serve to extract route setup into a testable function, (3) mocking rice.MustFindBox, (4) using feeds from https://trenchant.org/feeds.txt as static test fixtures for integration tests.","type":"cleanup","status":"open","priority":3,"labels":null,"assignee":"","created":"2026-02-13T03:54:30.298141982Z","updated":"2026-02-13T03:54:30.298141982Z"} {"id":"NK-op5594","title":"Ensure 80% Frontend Test Coverage","description":"Configure coverage reporting in vitest and ensure the frontend codebase maintains at least 80% test coverage.","type":"task","status":"closed","priority":2,"labels":null,"assignee":"","created":"2026-02-13T05:46:24.13314466Z","updated":"2026-02-13T05:50:46.728239299Z"} {"id":"NK-ric1zs","title":"Migrate frontend to /api/ endpoints","description":"The backend now provides a clean REST API at /api/. Update the frontend UI to use these new endpoints instead of the legacy backward-compatibility routes (/stream/, /feed/, etc.). This will allow for cleaner separation and better utilization of proper REST patterns.","type":"cleanup","status":"open","priority":3,"labels":null,"assignee":"","created":"2026-02-13T04:26:55.864725765Z","updated":"2026-02-13T04:26:55.864725765Z"} @@ -24,4 +25,5 @@ {"id":"NK-dlvmiyc","from_ticket_id":"NK-7tzbql","to_ticket_id":"NK-bsdwqz","type":"created_from","created":"2026-02-13T05:02:57.392616851Z"} {"id":"NK-dm75oc8","from_ticket_id":"NK-3om7x2","to_ticket_id":"NK-zt4e32","type":"created_from","created":"2026-02-13T05:59:46.169842933Z"} {"id":"NK-dofihuz","from_ticket_id":"NK-0ppv3f","to_ticket_id":"NK-t0nmbj","type":"created_from","created":"2026-02-13T05:44:01.640770816Z"} +{"id":"NK-dw8luqe","from_ticket_id":"NK-ek0cox","to_ticket_id":"NK-3om7x2","type":"created_from","created":"2026-02-13T14:55:14.832352853Z"} {"id":"NK-dwav3hh","from_ticket_id":"NK-6q9nyg","to_ticket_id":"NK-x924bu","type":"created_from","created":"2026-02-13T03:54:37.639569082Z"} diff --git a/frontend/coverage/clover.xml b/frontend/coverage/clover.xml index 5e830d2..8251808 100644 --- a/frontend/coverage/clover.xml +++ b/frontend/coverage/clover.xml @@ -1,7 +1,7 @@ - - - + + + @@ -22,33 +22,59 @@ - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + - + - - - - + + + + + diff --git a/frontend/coverage/coverage-final.json b/frontend/coverage/coverage-final.json index 1fe79ba..8fc3ade 100644 --- a/frontend/coverage/coverage-final.json +++ b/frontend/coverage/coverage-final.json @@ -1,7 +1,9 @@ {"/Users/adam/workspace/vibecode/neko/frontend/src/App.css": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/App.css","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":0,"seen":{}}} -,"/Users/adam/workspace/vibecode/neko/frontend/src/App.tsx": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/App.tsx","statementMap":{"0":{"start":{"line":8,"column":22},"end":{"line":8,"column":null}},"1":{"start":{"line":9,"column":8},"end":{"line":9,"column":null}},"2":{"start":{"line":11,"column":2},"end":{"line":21,"column":null}},"3":{"start":{"line":12,"column":4},"end":{"line":20,"column":null}},"4":{"start":{"line":14,"column":8},"end":{"line":18,"column":null}},"5":{"start":{"line":15,"column":10},"end":{"line":15,"column":null}},"6":{"start":{"line":17,"column":10},"end":{"line":17,"column":null}},"7":{"start":{"line":20,"column":19},"end":{"line":20,"column":33}},"8":{"start":{"line":23,"column":2},"end":{"line":25,"column":null}},"9":{"start":{"line":24,"column":4},"end":{"line":24,"column":null}},"10":{"start":{"line":27,"column":2},"end":{"line":29,"column":null}},"11":{"start":{"line":28,"column":4},"end":{"line":28,"column":null}},"12":{"start":{"line":31,"column":2},"end":{"line":31,"column":null}},"13":{"start":{"line":37,"column":2},"end":{"line":53,"column":null}},"14":{"start":{"line":58,"column":2},"end":{"line":71,"column":null}}},"fnMap":{"0":{"name":"RequireAuth","decl":{"start":{"line":7,"column":9},"end":{"line":7,"column":21}},"loc":{"start":{"line":7,"column":69},"end":{"line":32,"column":null}},"line":7},"1":{"name":"(anonymous_1)","decl":{"start":{"line":11,"column":12},"end":{"line":11,"column":18}},"loc":{"start":{"line":11,"column":18},"end":{"line":21,"column":5}},"line":11},"2":{"name":"(anonymous_2)","decl":{"start":{"line":13,"column":12},"end":{"line":13,"column":13}},"loc":{"start":{"line":13,"column":21},"end":{"line":19,"column":7}},"line":13},"3":{"name":"(anonymous_3)","decl":{"start":{"line":20,"column":13},"end":{"line":20,"column":19}},"loc":{"start":{"line":20,"column":19},"end":{"line":20,"column":33}},"line":20},"4":{"name":"Dashboard","decl":{"start":{"line":36,"column":9},"end":{"line":36,"column":21}},"loc":{"start":{"line":36,"column":21},"end":{"line":55,"column":null}},"line":36},"5":{"name":"App","decl":{"start":{"line":57,"column":9},"end":{"line":57,"column":15}},"loc":{"start":{"line":57,"column":15},"end":{"line":73,"column":null}},"line":57}},"branchMap":{"0":{"loc":{"start":{"line":14,"column":8},"end":{"line":18,"column":null}},"type":"if","locations":[{"start":{"line":14,"column":8},"end":{"line":18,"column":null}},{"start":{"line":16,"column":15},"end":{"line":18,"column":null}}],"line":14},"1":{"loc":{"start":{"line":23,"column":2},"end":{"line":25,"column":null}},"type":"if","locations":[{"start":{"line":23,"column":2},"end":{"line":25,"column":null}},{"start":{},"end":{}}],"line":23},"2":{"loc":{"start":{"line":27,"column":2},"end":{"line":29,"column":null}},"type":"if","locations":[{"start":{"line":27,"column":2},"end":{"line":29,"column":null}},{"start":{},"end":{}}],"line":27}},"s":{"0":2,"1":2,"2":2,"3":1,"4":1,"5":1,"6":0,"7":0,"8":2,"9":1,"10":1,"11":0,"12":1,"13":1,"14":2},"f":{"0":2,"1":1,"2":1,"3":0,"4":1,"5":2},"b":{"0":[1,0],"1":[1,1],"2":[0,1]},"meta":{"lastBranch":3,"lastFunction":6,"lastStatement":15,"seen":{"f:7:9:7:21":0,"s:8:22:8:Infinity":0,"s:9:8:9:Infinity":1,"s:11:2:21:Infinity":2,"f:11:12:11:18":1,"s:12:4:20:Infinity":3,"f:13:12:13:13":2,"b:14:8:18:Infinity:16:15:18:Infinity":0,"s:14:8:18:Infinity":4,"s:15:10:15:Infinity":5,"s:17:10:17:Infinity":6,"f:20:13:20:19":3,"s:20:19:20:33":7,"b:23:2:25:Infinity:undefined:undefined:undefined:undefined":1,"s:23:2:25:Infinity":8,"s:24:4:24:Infinity":9,"b:27:2:29:Infinity:undefined:undefined:undefined:undefined":2,"s:27:2:29:Infinity":10,"s:28:4:28:Infinity":11,"s:31:2:31:Infinity":12,"f:36:9:36:21":4,"s:37:2:53:Infinity":13,"f:57:9:57:15":5,"s:58:2:71:Infinity":14}}} +,"/Users/adam/workspace/vibecode/neko/frontend/src/App.tsx": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/App.tsx","statementMap":{"0":{"start":{"line":8,"column":22},"end":{"line":8,"column":null}},"1":{"start":{"line":9,"column":8},"end":{"line":9,"column":null}},"2":{"start":{"line":11,"column":2},"end":{"line":21,"column":null}},"3":{"start":{"line":12,"column":4},"end":{"line":20,"column":null}},"4":{"start":{"line":14,"column":8},"end":{"line":18,"column":null}},"5":{"start":{"line":15,"column":10},"end":{"line":15,"column":null}},"6":{"start":{"line":17,"column":10},"end":{"line":17,"column":null}},"7":{"start":{"line":20,"column":19},"end":{"line":20,"column":33}},"8":{"start":{"line":23,"column":2},"end":{"line":25,"column":null}},"9":{"start":{"line":24,"column":4},"end":{"line":24,"column":null}},"10":{"start":{"line":27,"column":2},"end":{"line":29,"column":null}},"11":{"start":{"line":28,"column":4},"end":{"line":28,"column":null}},"12":{"start":{"line":31,"column":2},"end":{"line":31,"column":null}},"13":{"start":{"line":38,"column":2},"end":{"line":57,"column":null}},"14":{"start":{"line":62,"column":2},"end":{"line":75,"column":null}}},"fnMap":{"0":{"name":"RequireAuth","decl":{"start":{"line":7,"column":9},"end":{"line":7,"column":21}},"loc":{"start":{"line":7,"column":69},"end":{"line":32,"column":null}},"line":7},"1":{"name":"(anonymous_1)","decl":{"start":{"line":11,"column":12},"end":{"line":11,"column":18}},"loc":{"start":{"line":11,"column":18},"end":{"line":21,"column":5}},"line":11},"2":{"name":"(anonymous_2)","decl":{"start":{"line":13,"column":12},"end":{"line":13,"column":13}},"loc":{"start":{"line":13,"column":21},"end":{"line":19,"column":7}},"line":13},"3":{"name":"(anonymous_3)","decl":{"start":{"line":20,"column":13},"end":{"line":20,"column":19}},"loc":{"start":{"line":20,"column":19},"end":{"line":20,"column":33}},"line":20},"4":{"name":"Dashboard","decl":{"start":{"line":37,"column":9},"end":{"line":37,"column":21}},"loc":{"start":{"line":37,"column":21},"end":{"line":59,"column":null}},"line":37},"5":{"name":"App","decl":{"start":{"line":61,"column":9},"end":{"line":61,"column":15}},"loc":{"start":{"line":61,"column":15},"end":{"line":77,"column":null}},"line":61}},"branchMap":{"0":{"loc":{"start":{"line":14,"column":8},"end":{"line":18,"column":null}},"type":"if","locations":[{"start":{"line":14,"column":8},"end":{"line":18,"column":null}},{"start":{"line":16,"column":15},"end":{"line":18,"column":null}}],"line":14},"1":{"loc":{"start":{"line":23,"column":2},"end":{"line":25,"column":null}},"type":"if","locations":[{"start":{"line":23,"column":2},"end":{"line":25,"column":null}},{"start":{},"end":{}}],"line":23},"2":{"loc":{"start":{"line":27,"column":2},"end":{"line":29,"column":null}},"type":"if","locations":[{"start":{"line":27,"column":2},"end":{"line":29,"column":null}},{"start":{},"end":{}}],"line":27}},"s":{"0":2,"1":2,"2":2,"3":1,"4":1,"5":1,"6":0,"7":0,"8":2,"9":1,"10":1,"11":0,"12":1,"13":1,"14":2},"f":{"0":2,"1":1,"2":1,"3":0,"4":1,"5":2},"b":{"0":[1,0],"1":[1,1],"2":[0,1]},"meta":{"lastBranch":3,"lastFunction":6,"lastStatement":15,"seen":{"f:7:9:7:21":0,"s:8:22:8:Infinity":0,"s:9:8:9:Infinity":1,"s:11:2:21:Infinity":2,"f:11:12:11:18":1,"s:12:4:20:Infinity":3,"f:13:12:13:13":2,"b:14:8:18:Infinity:16:15:18:Infinity":0,"s:14:8:18:Infinity":4,"s:15:10:15:Infinity":5,"s:17:10:17:Infinity":6,"f:20:13:20:19":3,"s:20:19:20:33":7,"b:23:2:25:Infinity:undefined:undefined:undefined:undefined":1,"s:23:2:25:Infinity":8,"s:24:4:24:Infinity":9,"b:27:2:29:Infinity:undefined:undefined:undefined:undefined":2,"s:27:2:29:Infinity":10,"s:28:4:28:Infinity":11,"s:31:2:31:Infinity":12,"f:37:9:37:21":4,"s:38:2:57:Infinity":13,"f:61:9:61:15":5,"s:62:2:75:Infinity":14}}} +,"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItems.css": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItems.css","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":0,"seen":{}}} +,"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItems.tsx": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItems.tsx","statementMap":{"0":{"start":{"line":7,"column":19},"end":{"line":7,"column":null}},"1":{"start":{"line":8,"column":26},"end":{"line":8,"column":null}},"2":{"start":{"line":9,"column":30},"end":{"line":9,"column":null}},"3":{"start":{"line":10,"column":26},"end":{"line":10,"column":null}},"4":{"start":{"line":12,"column":4},"end":{"line":35,"column":null}},"5":{"start":{"line":13,"column":8},"end":{"line":13,"column":null}},"6":{"start":{"line":14,"column":8},"end":{"line":14,"column":null}},"7":{"start":{"line":16,"column":20},"end":{"line":18,"column":null}},"8":{"start":{"line":20,"column":8},"end":{"line":34,"column":null}},"9":{"start":{"line":22,"column":16},"end":{"line":24,"column":null}},"10":{"start":{"line":23,"column":20},"end":{"line":23,"column":null}},"11":{"start":{"line":25,"column":16},"end":{"line":25,"column":null}},"12":{"start":{"line":28,"column":16},"end":{"line":28,"column":null}},"13":{"start":{"line":29,"column":16},"end":{"line":29,"column":null}},"14":{"start":{"line":32,"column":16},"end":{"line":32,"column":null}},"15":{"start":{"line":33,"column":16},"end":{"line":33,"column":null}},"16":{"start":{"line":37,"column":4},"end":{"line":37,"column":null}},"17":{"start":{"line":37,"column":17},"end":{"line":37,"column":null}},"18":{"start":{"line":38,"column":4},"end":{"line":38,"column":null}},"19":{"start":{"line":38,"column":15},"end":{"line":38,"column":null}},"20":{"start":{"line":40,"column":4},"end":{"line":64,"column":null}},"21":{"start":{"line":49,"column":24},"end":{"line":60,"column":null}}},"fnMap":{"0":{"name":"FeedItems","decl":{"start":{"line":6,"column":24},"end":{"line":6,"column":36}},"loc":{"start":{"line":6,"column":36},"end":{"line":66,"column":null}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":12,"column":14},"end":{"line":12,"column":20}},"loc":{"start":{"line":12,"column":20},"end":{"line":35,"column":7}},"line":12},"2":{"name":"(anonymous_2)","decl":{"start":{"line":21,"column":18},"end":{"line":21,"column":19}},"loc":{"start":{"line":21,"column":27},"end":{"line":26,"column":13}},"line":21},"3":{"name":"(anonymous_3)","decl":{"start":{"line":27,"column":18},"end":{"line":27,"column":19}},"loc":{"start":{"line":27,"column":28},"end":{"line":30,"column":13}},"line":27},"4":{"name":"(anonymous_4)","decl":{"start":{"line":31,"column":19},"end":{"line":31,"column":20}},"loc":{"start":{"line":31,"column":28},"end":{"line":34,"column":13}},"line":31},"5":{"name":"(anonymous_5)","decl":{"start":{"line":48,"column":31},"end":{"line":48,"column":32}},"loc":{"start":{"line":49,"column":24},"end":{"line":60,"column":null}},"line":49}},"branchMap":{"0":{"loc":{"start":{"line":16,"column":20},"end":{"line":18,"column":null}},"type":"cond-expr","locations":[{"start":{"line":17,"column":14},"end":{"line":17,"column":null}},{"start":{"line":18,"column":14},"end":{"line":18,"column":null}}],"line":16},"1":{"loc":{"start":{"line":22,"column":16},"end":{"line":24,"column":null}},"type":"if","locations":[{"start":{"line":22,"column":16},"end":{"line":24,"column":null}},{"start":{},"end":{}}],"line":22},"2":{"loc":{"start":{"line":37,"column":4},"end":{"line":37,"column":null}},"type":"if","locations":[{"start":{"line":37,"column":4},"end":{"line":37,"column":null}},{"start":{},"end":{}}],"line":37},"3":{"loc":{"start":{"line":38,"column":4},"end":{"line":38,"column":null}},"type":"if","locations":[{"start":{"line":38,"column":4},"end":{"line":38,"column":null}},{"start":{},"end":{}}],"line":38},"4":{"loc":{"start":{"line":44,"column":13},"end":{"line":62,"column":null}},"type":"cond-expr","locations":[{"start":{"line":45,"column":16},"end":{"line":45,"column":null}},{"start":{"line":47,"column":16},"end":{"line":62,"column":null}}],"line":44},"5":{"loc":{"start":{"line":49,"column":62},"end":{"line":49,"column":91}},"type":"cond-expr","locations":[{"start":{"line":49,"column":74},"end":{"line":49,"column":83}},{"start":{"line":49,"column":83},"end":{"line":49,"column":91}}],"line":49},"6":{"loc":{"start":{"line":51,"column":33},"end":{"line":51,"column":null}},"type":"binary-expr","locations":[{"start":{"line":51,"column":33},"end":{"line":51,"column":47}},{"start":{"line":51,"column":47},"end":{"line":51,"column":null}}],"line":51},"7":{"loc":{"start":{"line":55,"column":33},"end":{"line":55,"column":null}},"type":"binary-expr","locations":[{"start":{"line":55,"column":33},"end":{"line":55,"column":52}},{"start":{"line":55,"column":52},"end":{"line":55,"column":null}}],"line":55},"8":{"loc":{"start":{"line":57,"column":29},"end":{"line":58,"column":null}},"type":"binary-expr","locations":[{"start":{"line":57,"column":29},"end":{"line":57,"column":null}},{"start":{"line":58,"column":32},"end":{"line":58,"column":null}}],"line":57}},"s":{"0":3,"1":3,"2":3,"3":3,"4":3,"5":2,"6":2,"7":2,"8":2,"9":1,"10":0,"11":1,"12":1,"13":1,"14":0,"15":0,"16":3,"17":2,"18":1,"19":1,"20":1,"21":2},"f":{"0":3,"1":2,"2":1,"3":1,"4":0,"5":2},"b":{"0":[2,0],"1":[0,1],"2":[2,1],"3":[0,1],"4":[0,1],"5":[1,1],"6":[2,0],"7":[2,0],"8":[2,0]},"meta":{"lastBranch":9,"lastFunction":6,"lastStatement":22,"seen":{"f:6:24:6:36":0,"s:7:19:7:Infinity":0,"s:8:26:8:Infinity":1,"s:9:30:9:Infinity":2,"s:10:26:10:Infinity":3,"s:12:4:35:Infinity":4,"f:12:14:12:20":1,"s:13:8:13:Infinity":5,"s:14:8:14:Infinity":6,"s:16:20:18:Infinity":7,"b:17:14:17:Infinity:18:14:18:Infinity":0,"s:20:8:34:Infinity":8,"f:21:18:21:19":2,"b:22:16:24:Infinity:undefined:undefined:undefined:undefined":1,"s:22:16:24:Infinity":9,"s:23:20:23:Infinity":10,"s:25:16:25:Infinity":11,"f:27:18:27:19":3,"s:28:16:28:Infinity":12,"s:29:16:29:Infinity":13,"f:31:19:31:20":4,"s:32:16:32:Infinity":14,"s:33:16:33:Infinity":15,"b:37:4:37:Infinity:undefined:undefined:undefined:undefined":2,"s:37:4:37:Infinity":16,"s:37:17:37:Infinity":17,"b:38:4:38:Infinity:undefined:undefined:undefined:undefined":3,"s:38:4:38:Infinity":18,"s:38:15:38:Infinity":19,"s:40:4:64:Infinity":20,"b:45:16:45:Infinity:47:16:62:Infinity":4,"f:48:31:48:32":5,"s:49:24:60:Infinity":21,"b:49:74:49:83:49:83:49:91":5,"b:51:33:51:47:51:47:51:Infinity":6,"b:55:33:55:52:55:52:55:Infinity":7,"b:57:29:57:Infinity:58:32:58:Infinity":8}}} ,"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.css": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.css","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":0,"seen":{}}} -,"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.tsx": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.tsx","statementMap":{"0":{"start":{"line":6,"column":26},"end":{"line":6,"column":null}},"1":{"start":{"line":7,"column":30},"end":{"line":7,"column":null}},"2":{"start":{"line":8,"column":26},"end":{"line":8,"column":null}},"3":{"start":{"line":10,"column":4},"end":{"line":26,"column":null}},"4":{"start":{"line":11,"column":8},"end":{"line":25,"column":null}},"5":{"start":{"line":13,"column":16},"end":{"line":15,"column":null}},"6":{"start":{"line":14,"column":20},"end":{"line":14,"column":null}},"7":{"start":{"line":16,"column":16},"end":{"line":16,"column":null}},"8":{"start":{"line":19,"column":16},"end":{"line":19,"column":null}},"9":{"start":{"line":20,"column":16},"end":{"line":20,"column":null}},"10":{"start":{"line":23,"column":16},"end":{"line":23,"column":null}},"11":{"start":{"line":24,"column":16},"end":{"line":24,"column":null}},"12":{"start":{"line":28,"column":4},"end":{"line":28,"column":null}},"13":{"start":{"line":28,"column":17},"end":{"line":28,"column":null}},"14":{"start":{"line":29,"column":4},"end":{"line":29,"column":null}},"15":{"start":{"line":29,"column":15},"end":{"line":29,"column":null}},"16":{"start":{"line":31,"column":4},"end":{"line":48,"column":null}},"17":{"start":{"line":39,"column":24},"end":{"line":44,"column":null}}},"fnMap":{"0":{"name":"FeedList","decl":{"start":{"line":5,"column":24},"end":{"line":5,"column":35}},"loc":{"start":{"line":5,"column":35},"end":{"line":50,"column":null}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":10,"column":14},"end":{"line":10,"column":20}},"loc":{"start":{"line":10,"column":20},"end":{"line":26,"column":7}},"line":10},"2":{"name":"(anonymous_2)","decl":{"start":{"line":12,"column":18},"end":{"line":12,"column":19}},"loc":{"start":{"line":12,"column":27},"end":{"line":17,"column":13}},"line":12},"3":{"name":"(anonymous_3)","decl":{"start":{"line":18,"column":18},"end":{"line":18,"column":19}},"loc":{"start":{"line":18,"column":28},"end":{"line":21,"column":13}},"line":18},"4":{"name":"(anonymous_4)","decl":{"start":{"line":22,"column":19},"end":{"line":22,"column":20}},"loc":{"start":{"line":22,"column":28},"end":{"line":25,"column":13}},"line":22},"5":{"name":"(anonymous_5)","decl":{"start":{"line":38,"column":31},"end":{"line":38,"column":32}},"loc":{"start":{"line":39,"column":24},"end":{"line":44,"column":null}},"line":39}},"branchMap":{"0":{"loc":{"start":{"line":13,"column":16},"end":{"line":15,"column":null}},"type":"if","locations":[{"start":{"line":13,"column":16},"end":{"line":15,"column":null}},{"start":{},"end":{}}],"line":13},"1":{"loc":{"start":{"line":28,"column":4},"end":{"line":28,"column":null}},"type":"if","locations":[{"start":{"line":28,"column":4},"end":{"line":28,"column":null}},{"start":{},"end":{}}],"line":28},"2":{"loc":{"start":{"line":29,"column":4},"end":{"line":29,"column":null}},"type":"if","locations":[{"start":{"line":29,"column":4},"end":{"line":29,"column":null}},{"start":{},"end":{}}],"line":29},"3":{"loc":{"start":{"line":34,"column":13},"end":{"line":46,"column":null}},"type":"cond-expr","locations":[{"start":{"line":35,"column":16},"end":{"line":35,"column":null}},{"start":{"line":37,"column":16},"end":{"line":46,"column":null}}],"line":34},"4":{"loc":{"start":{"line":41,"column":33},"end":{"line":41,"column":null}},"type":"binary-expr","locations":[{"start":{"line":41,"column":33},"end":{"line":41,"column":47}},{"start":{"line":41,"column":47},"end":{"line":41,"column":null}}],"line":41},"5":{"loc":{"start":{"line":43,"column":29},"end":{"line":43,"column":null}},"type":"binary-expr","locations":[{"start":{"line":43,"column":29},"end":{"line":43,"column":46}},{"start":{"line":43,"column":46},"end":{"line":43,"column":null}}],"line":43}},"s":{"0":9,"1":9,"2":9,"3":9,"4":5,"5":3,"6":0,"7":3,"8":3,"9":3,"10":1,"11":1,"12":9,"13":5,"14":4,"15":4,"16":3,"17":2},"f":{"0":9,"1":5,"2":3,"3":3,"4":1,"5":2},"b":{"0":[0,3],"1":[5,4],"2":[1,3],"3":[2,1],"4":[2,0],"5":[2,2]},"meta":{"lastBranch":6,"lastFunction":6,"lastStatement":18,"seen":{"f:5:24:5:35":0,"s:6:26:6:Infinity":0,"s:7:30:7:Infinity":1,"s:8:26:8:Infinity":2,"s:10:4:26:Infinity":3,"f:10:14:10:20":1,"s:11:8:25:Infinity":4,"f:12:18:12:19":2,"b:13:16:15:Infinity:undefined:undefined:undefined:undefined":0,"s:13:16:15:Infinity":5,"s:14:20:14:Infinity":6,"s:16:16:16:Infinity":7,"f:18:18:18:19":3,"s:19:16:19:Infinity":8,"s:20:16:20:Infinity":9,"f:22:19:22:20":4,"s:23:16:23:Infinity":10,"s:24:16:24:Infinity":11,"b:28:4:28:Infinity:undefined:undefined:undefined:undefined":1,"s:28:4:28:Infinity":12,"s:28:17:28:Infinity":13,"b:29:4:29:Infinity:undefined:undefined:undefined:undefined":2,"s:29:4:29:Infinity":14,"s:29:15:29:Infinity":15,"s:31:4:48:Infinity":16,"b:35:16:35:Infinity:37:16:46:Infinity":3,"f:38:31:38:32":5,"s:39:24:44:Infinity":17,"b:41:33:41:47:41:47:41:Infinity":4,"b:43:29:43:46:43:46:43:Infinity":5}}} +,"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.tsx": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.tsx","statementMap":{"0":{"start":{"line":7,"column":26},"end":{"line":7,"column":null}},"1":{"start":{"line":8,"column":30},"end":{"line":8,"column":null}},"2":{"start":{"line":9,"column":26},"end":{"line":9,"column":null}},"3":{"start":{"line":11,"column":4},"end":{"line":27,"column":null}},"4":{"start":{"line":12,"column":8},"end":{"line":26,"column":null}},"5":{"start":{"line":14,"column":16},"end":{"line":16,"column":null}},"6":{"start":{"line":15,"column":20},"end":{"line":15,"column":null}},"7":{"start":{"line":17,"column":16},"end":{"line":17,"column":null}},"8":{"start":{"line":20,"column":16},"end":{"line":20,"column":null}},"9":{"start":{"line":21,"column":16},"end":{"line":21,"column":null}},"10":{"start":{"line":24,"column":16},"end":{"line":24,"column":null}},"11":{"start":{"line":25,"column":16},"end":{"line":25,"column":null}},"12":{"start":{"line":29,"column":4},"end":{"line":29,"column":null}},"13":{"start":{"line":29,"column":17},"end":{"line":29,"column":null}},"14":{"start":{"line":30,"column":4},"end":{"line":30,"column":null}},"15":{"start":{"line":30,"column":15},"end":{"line":30,"column":null}},"16":{"start":{"line":32,"column":4},"end":{"line":49,"column":null}},"17":{"start":{"line":40,"column":24},"end":{"line":45,"column":null}}},"fnMap":{"0":{"name":"FeedList","decl":{"start":{"line":6,"column":24},"end":{"line":6,"column":35}},"loc":{"start":{"line":6,"column":35},"end":{"line":51,"column":null}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":11,"column":14},"end":{"line":11,"column":20}},"loc":{"start":{"line":11,"column":20},"end":{"line":27,"column":7}},"line":11},"2":{"name":"(anonymous_2)","decl":{"start":{"line":13,"column":18},"end":{"line":13,"column":19}},"loc":{"start":{"line":13,"column":27},"end":{"line":18,"column":13}},"line":13},"3":{"name":"(anonymous_3)","decl":{"start":{"line":19,"column":18},"end":{"line":19,"column":19}},"loc":{"start":{"line":19,"column":28},"end":{"line":22,"column":13}},"line":19},"4":{"name":"(anonymous_4)","decl":{"start":{"line":23,"column":19},"end":{"line":23,"column":20}},"loc":{"start":{"line":23,"column":28},"end":{"line":26,"column":13}},"line":23},"5":{"name":"(anonymous_5)","decl":{"start":{"line":39,"column":31},"end":{"line":39,"column":32}},"loc":{"start":{"line":40,"column":24},"end":{"line":45,"column":null}},"line":40}},"branchMap":{"0":{"loc":{"start":{"line":14,"column":16},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":14,"column":16},"end":{"line":16,"column":null}},{"start":{},"end":{}}],"line":14},"1":{"loc":{"start":{"line":29,"column":4},"end":{"line":29,"column":null}},"type":"if","locations":[{"start":{"line":29,"column":4},"end":{"line":29,"column":null}},{"start":{},"end":{}}],"line":29},"2":{"loc":{"start":{"line":30,"column":4},"end":{"line":30,"column":null}},"type":"if","locations":[{"start":{"line":30,"column":4},"end":{"line":30,"column":null}},{"start":{},"end":{}}],"line":30},"3":{"loc":{"start":{"line":35,"column":13},"end":{"line":47,"column":null}},"type":"cond-expr","locations":[{"start":{"line":36,"column":16},"end":{"line":36,"column":null}},{"start":{"line":38,"column":16},"end":{"line":47,"column":null}}],"line":35},"4":{"loc":{"start":{"line":42,"column":33},"end":{"line":42,"column":null}},"type":"binary-expr","locations":[{"start":{"line":42,"column":33},"end":{"line":42,"column":47}},{"start":{"line":42,"column":47},"end":{"line":42,"column":null}}],"line":42},"5":{"loc":{"start":{"line":44,"column":29},"end":{"line":44,"column":null}},"type":"binary-expr","locations":[{"start":{"line":44,"column":29},"end":{"line":44,"column":46}},{"start":{"line":44,"column":46},"end":{"line":44,"column":null}}],"line":44}},"s":{"0":8,"1":8,"2":8,"3":8,"4":5,"5":3,"6":0,"7":3,"8":3,"9":3,"10":1,"11":1,"12":8,"13":5,"14":3,"15":3,"16":2,"17":2},"f":{"0":8,"1":5,"2":3,"3":3,"4":1,"5":2},"b":{"0":[0,3],"1":[5,3],"2":[1,2],"3":[1,1],"4":[2,0],"5":[2,2]},"meta":{"lastBranch":6,"lastFunction":6,"lastStatement":18,"seen":{"f:6:24:6:35":0,"s:7:26:7:Infinity":0,"s:8:30:8:Infinity":1,"s:9:26:9:Infinity":2,"s:11:4:27:Infinity":3,"f:11:14:11:20":1,"s:12:8:26:Infinity":4,"f:13:18:13:19":2,"b:14:16:16:Infinity:undefined:undefined:undefined:undefined":0,"s:14:16:16:Infinity":5,"s:15:20:15:Infinity":6,"s:17:16:17:Infinity":7,"f:19:18:19:19":3,"s:20:16:20:Infinity":8,"s:21:16:21:Infinity":9,"f:23:19:23:20":4,"s:24:16:24:Infinity":10,"s:25:16:25:Infinity":11,"b:29:4:29:Infinity:undefined:undefined:undefined:undefined":1,"s:29:4:29:Infinity":12,"s:29:17:29:Infinity":13,"b:30:4:30:Infinity:undefined:undefined:undefined:undefined":2,"s:30:4:30:Infinity":14,"s:30:15:30:Infinity":15,"s:32:4:49:Infinity":16,"b:36:16:36:Infinity:38:16:47:Infinity":3,"f:39:31:39:32":5,"s:40:24:45:Infinity":17,"b:42:33:42:47:42:47:42:Infinity":4,"b:44:29:44:46:44:46:44:Infinity":5}}} ,"/Users/adam/workspace/vibecode/neko/frontend/src/components/Login.css": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/Login.css","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":0,"seen":{}}} ,"/Users/adam/workspace/vibecode/neko/frontend/src/components/Login.tsx": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/Login.tsx","statementMap":{"0":{"start":{"line":6,"column":32},"end":{"line":6,"column":null}},"1":{"start":{"line":7,"column":26},"end":{"line":7,"column":null}},"2":{"start":{"line":8,"column":10},"end":{"line":8,"column":null}},"3":{"start":{"line":10,"column":25},"end":{"line":33,"column":null}},"4":{"start":{"line":11,"column":8},"end":{"line":11,"column":null}},"5":{"start":{"line":12,"column":8},"end":{"line":12,"column":null}},"6":{"start":{"line":14,"column":8},"end":{"line":32,"column":null}},"7":{"start":{"line":16,"column":27},"end":{"line":16,"column":null}},"8":{"start":{"line":17,"column":12},"end":{"line":17,"column":null}},"9":{"start":{"line":19,"column":24},"end":{"line":22,"column":null}},"10":{"start":{"line":24,"column":12},"end":{"line":29,"column":null}},"11":{"start":{"line":25,"column":16},"end":{"line":25,"column":null}},"12":{"start":{"line":27,"column":29},"end":{"line":27,"column":null}},"13":{"start":{"line":28,"column":16},"end":{"line":28,"column":null}},"14":{"start":{"line":31,"column":12},"end":{"line":31,"column":null}},"15":{"start":{"line":35,"column":4},"end":{"line":52,"column":null}},"16":{"start":{"line":45,"column":41},"end":{"line":45,"column":null}}},"fnMap":{"0":{"name":"Login","decl":{"start":{"line":5,"column":24},"end":{"line":5,"column":32}},"loc":{"start":{"line":5,"column":32},"end":{"line":54,"column":null}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":10,"column":25},"end":{"line":10,"column":32}},"loc":{"start":{"line":10,"column":49},"end":{"line":33,"column":null}},"line":10},"2":{"name":"(anonymous_2)","decl":{"start":{"line":45,"column":34},"end":{"line":45,"column":35}},"loc":{"start":{"line":45,"column":41},"end":{"line":45,"column":null}},"line":45}},"branchMap":{"0":{"loc":{"start":{"line":24,"column":12},"end":{"line":29,"column":null}},"type":"if","locations":[{"start":{"line":24,"column":12},"end":{"line":29,"column":null}},{"start":{"line":26,"column":19},"end":{"line":29,"column":null}}],"line":24},"1":{"loc":{"start":{"line":28,"column":25},"end":{"line":28,"column":55}},"type":"binary-expr","locations":[{"start":{"line":28,"column":25},"end":{"line":28,"column":41}},{"start":{"line":28,"column":41},"end":{"line":28,"column":55}}],"line":28},"2":{"loc":{"start":{"line":49,"column":17},"end":{"line":49,"column":null}},"type":"binary-expr","locations":[{"start":{"line":49,"column":17},"end":{"line":49,"column":26}},{"start":{"line":49,"column":26},"end":{"line":49,"column":null}}],"line":49}},"s":{"0":14,"1":14,"2":14,"3":14,"4":3,"5":3,"6":3,"7":3,"8":3,"9":3,"10":2,"11":1,"12":1,"13":1,"14":1,"15":14,"16":3},"f":{"0":14,"1":3,"2":3},"b":{"0":[1,1],"1":[1,0],"2":[14,2]},"meta":{"lastBranch":3,"lastFunction":3,"lastStatement":17,"seen":{"f:5:24:5:32":0,"s:6:32:6:Infinity":0,"s:7:26:7:Infinity":1,"s:8:10:8:Infinity":2,"s:10:25:33:Infinity":3,"f:10:25:10:32":1,"s:11:8:11:Infinity":4,"s:12:8:12:Infinity":5,"s:14:8:32:Infinity":6,"s:16:27:16:Infinity":7,"s:17:12:17:Infinity":8,"s:19:24:22:Infinity":9,"b:24:12:29:Infinity:26:19:29:Infinity":0,"s:24:12:29:Infinity":10,"s:25:16:25:Infinity":11,"s:27:29:27:Infinity":12,"s:28:16:28:Infinity":13,"b:28:25:28:41:28:41:28:55":1,"s:31:12:31:Infinity":14,"s:35:4:52:Infinity":15,"f:45:34:45:35":2,"s:45:41:45:Infinity":16,"b:49:17:49:26:49:26:49:Infinity":2}}} } diff --git a/frontend/coverage/index.html b/frontend/coverage/index.html index e90ae39..8011000 100644 --- a/frontend/coverage/index.html +++ b/frontend/coverage/index.html @@ -23,30 +23,30 @@
- 92% + 90.27% Statements - 46/50 + 65/72
- 79.16% + 71.42% Branches - 19/24 + 30/42
- 93.33% + 90.47% Functions - 14/15 + 19/21
- 91.66% + 89.7% Lines - 44/48 + 61/68
@@ -95,17 +95,17 @@ src/components - -
+ +
- 97.14% - 34/35 - 83.33% - 15/18 - 100% - 9/9 - 96.96% - 32/33 + 92.98% + 53/57 + 72.22% + 26/36 + 93.33% + 14/15 + 92.45% + 49/53 @@ -116,7 +116,7 @@ + + + + + + \ No newline at end of file diff --git a/frontend/coverage/src/components/FeedItems.tsx.html b/frontend/coverage/src/components/FeedItems.tsx.html new file mode 100644 index 0000000..85c9705 --- /dev/null +++ b/frontend/coverage/src/components/FeedItems.tsx.html @@ -0,0 +1,283 @@ + + + + + + Code coverage report for src/components/FeedItems.tsx + + + + + + + + + +
+
+

All files / src/components FeedItems.tsx

+
+ +
+ 86.36% + Statements + 19/22 +
+ + +
+ 61.11% + Branches + 11/18 +
+ + +
+ 83.33% + Functions + 5/6 +
+ + +
+ 85% + Lines + 17/20 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67  +  +  +  +  +  +3x +3x +3x +3x +  +3x +2x +2x +  +2x +  +  +  +2x +  +1x +  +  +1x +  +  +1x +1x +  +  +  +  +  +  +  +3x +1x +  +1x +  +  +  +  +  +  +  +  +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { useEffect, useState } from 'react';
+import { useParams } from 'react-router-dom';
+import type { Item } from '../types';
+import './FeedItems.css';
+ 
+export default function FeedItems() {
+    const { feedId } = useParams<{ feedId: string }>();
+    const [items, setItems] = useState<Item[]>([]);
+    const [loading, setLoading] = useState(true);
+    const [error, setError] = useState('');
+ 
+    useEffect(() => {
+        setLoading(true);
+        setError('');
+ 
+        const url = feedId
+            ? `/api/stream?feed_id=${feedId}`
+            : '/api/stream'; // Default or "all" view? For now let's assume we need a feedId or handle "all" logic later
+ 
+        fetch(url)
+            .then((res) => {
+                Iif (!res.ok) {
+                    throw new Error('Failed to fetch items');
+                }
+                return res.json();
+            })
+            .then((data) => {
+                setItems(data);
+                setLoading(false);
+            })
+            .catch((err) => {
+                setError(err.message);
+                setLoading(false);
+            });
+    }, [feedId]);
+ 
+    if (loading) return <div className="feed-items-loading">Loading items...</div>;
+    Iif (error) return <div className="feed-items-error">Error: {error}</div>;
+ 
+    return (
+        <div className="feed-items">
+            <h2>Items</h2>
+            {/* TODO: Add Feed Title here if possible, maybe pass from location state or fetch feed details */}
+            {items.length === 0 ? (
+                <p>No items found.</p>
+            ) : (
+                <ul className="item-list">
+                    {items.map((item) => (
+                        <li key={item._id} className={`item ${item.read ? 'read' : 'unread'}`}>
+                            <a href={item.url} target="_blank" rel="noopener noreferrer" className="item-title">
+                                {item.title || '(No Title)'}
+                            </a>
+                            <div className="item-meta">
+                                <span className="item-date">{new Date(item.publish_date).toLocaleDateString()}</span>
+                                {item.feed_title && <span className="item-feed"> - {item.feed_title}</span>}
+                            </div>
+                            {item.description && (
+                                <div className="item-description" dangerouslySetInnerHTML={{ __html: item.description }} />
+                            )}
+                        </li>
+                    ))}
+                </ul>
+            )}
+        </div>
+    );
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/src/components/FeedList.css.html b/frontend/coverage/src/components/FeedList.css.html index cd14b1b..f86a2d4 100644 --- a/frontend/coverage/src/components/FeedList.css.html +++ b/frontend/coverage/src/components/FeedList.css.html @@ -211,7 +211,7 @@