aboutsummaryrefslogtreecommitdiffstats
path: root/frontend/src/components/FeedItems.tsx
diff options
context:
space:
mode:
authorAdam Mathes <adam@adammathes.com>2026-02-13 06:55:21 -0800
committerAdam Mathes <adam@adammathes.com>2026-02-13 06:55:21 -0800
commit3ba71500bc2d60a00ca81b9439305029670f4d52 (patch)
tree5752a119effd739c62c80c2d15d4520c2e53eadf /frontend/src/components/FeedItems.tsx
parent2c3cad528a247c771bca136466337877f76f280f (diff)
downloadneko-3ba71500bc2d60a00ca81b9439305029670f4d52.tar.gz
neko-3ba71500bc2d60a00ca81b9439305029670f4d52.tar.bz2
neko-3ba71500bc2d60a00ca81b9439305029670f4d52.zip
Implement Frontend Feed Items View with tests
Diffstat (limited to 'frontend/src/components/FeedItems.tsx')
-rw-r--r--frontend/src/components/FeedItems.tsx66
1 files changed, 66 insertions, 0 deletions
diff --git a/frontend/src/components/FeedItems.tsx b/frontend/src/components/FeedItems.tsx
new file mode 100644
index 0000000..048bed7
--- /dev/null
+++ b/frontend/src/components/FeedItems.tsx
@@ -0,0 +1,66 @@
+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) => {
+ if (!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>;
+ if (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>
+ );
+}