From a5cd9538b0db731a0d0e10e58804ef8ad32211b7 Mon Sep 17 00:00:00 2001 From: Adam Mathes Date: Fri, 13 Feb 2026 07:13:18 -0800 Subject: Implement Frontend Settings with tests --- frontend/src/components/Settings.tsx | 121 +++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 frontend/src/components/Settings.tsx (limited to 'frontend/src/components/Settings.tsx') diff --git a/frontend/src/components/Settings.tsx b/frontend/src/components/Settings.tsx new file mode 100644 index 0000000..def8ffe --- /dev/null +++ b/frontend/src/components/Settings.tsx @@ -0,0 +1,121 @@ +import React, { useEffect, useState } from 'react'; +import type { Feed } from '../types'; +import './Settings.css'; + +export default function Settings() { + const [feeds, setFeeds] = useState([]); + const [newFeedUrl, setNewFeedUrl] = useState(''); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + fetchFeeds(); + }, []); + + const fetchFeeds = () => { + setLoading(true); + fetch('/api/feed/') + .then((res) => { + if (!res.ok) throw new Error('Failed to fetch feeds'); + return res.json(); + }) + .then((data) => { + setFeeds(data); + setLoading(false); + }) + .catch((err) => { + setError(err.message); + setLoading(false); + }); + }; + + const handleAddFeed = (e: React.FormEvent) => { + e.preventDefault(); + if (!newFeedUrl) return; + + setLoading(true); + fetch('/api/feed/', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ url: newFeedUrl }), + }) + .then((res) => { + if (!res.ok) throw new Error('Failed to add feed'); + return res.json(); + }) + .then(() => { + setNewFeedUrl(''); + fetchFeeds(); // Refresh list (or we could append if server returns full feed object) + }) + .catch((err) => { + setError(err.message); + setLoading(false); + }); + }; + + const handleDeleteFeed = (id: number) => { + if (!globalThis.confirm('Are you sure you want to delete this feed?')) return; + + setLoading(true); + fetch(`/api/feed/${id}`, { + method: 'DELETE', + }) + .then((res) => { + if (!res.ok) throw new Error('Failed to delete feed'); + setFeeds(feeds.filter((f) => f._id !== id)); + setLoading(false); + }) + .catch((err) => { + setError(err.message); + setLoading(false); + }); + }; + + return ( +
+

Settings

+ +
+

Add New Feed

+
+ setNewFeedUrl(e.target.value)} + placeholder="https://example.com/feed.xml" + required + className="feed-input" + disabled={loading} + /> + +
+ {error &&

{error}

} +
+ +
+

Manage Feeds

+ {loading &&

Loading...

} +
    + {feeds.map((feed) => ( +
  • +
    + {feed.title || '(No Title)'} + {feed.url} +
    + +
  • + ))} +
+
+
+ ); +} -- cgit v1.2.3