refactor(admin): migrate feeds.ts to feeds.tsx with JSX rendering

Convert the edit feed GET route from hono/html tagged template literals
to typed JSX using the <Layout> component. All CRUD routes and business
logic are preserved unchanged. textarea placeholder special characters
are now handled via JSX attribute escaping rather than &#10; entities.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Julien Herr
2026-05-22 13:17:31 +02:00
parent 996aa5e211
commit ecb85730e0
@@ -1,11 +1,10 @@
import { Hono } from "hono"; import { Hono } from "hono";
import { html } from "hono/html";
import { z } from "zod"; import { z } from "zod";
import { Env, FeedConfig, FeedMetadata, EmailData } from "../../types"; import { Env, FeedConfig, FeedMetadata, EmailData } from "../../types";
import { generateFeedId } from "../../utils/id-generator"; import { generateFeedId } from "../../utils/id-generator";
import { waitUntilSafe } from "../../utils/worker"; import { waitUntilSafe } from "../../utils/worker";
import { logger } from "../../lib/logger"; import { logger } from "../../lib/logger";
import { layout } from "./ui"; import { Layout } from "./ui";
import { import {
addFeedToList, addFeedToList,
updateFeedInList, updateFeedInList,
@@ -246,68 +245,66 @@ feedsRouter.get("/:feedId/edit", async (c) => {
} }
return c.html( return c.html(
layout( <Layout title="Edit Feed">
"Edit Feed",
html`
<div class="container fade-in"> <div class="container fade-in">
<div class="header-with-actions"> <div class="header-with-actions">
<div class="header-title"> <div class="header-title">
<h1>${feedConfig.title} - Edit Feed</h1> <h1>{feedConfig.title} - Edit Feed</h1>
</div> </div>
<div class="header-actions"> <div class="header-actions">
<a href="/admin" class="button button-secondary button-back" <a href="/admin" class="button button-secondary button-back">
>Back to Dashboard</a Back to Dashboard
> </a>
</div> </div>
</div> </div>
<div class="card"> <div class="card">
<form action="/admin/feeds/${feedId}/edit" method="post"> <form action={`/admin/feeds/${feedId}/edit`} method="post">
<div class="form-group"> <div class="form-group">
<label for="title">Feed Title</label> <label for="title">Feed Title</label>
<input <input
type="text" type="text"
id="title" id="title"
name="title" name="title"
value="${feedConfig.title}" value={feedConfig.title}
required required
/> />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="description">Description</label> <label for="description">Description</label>
<textarea id="description" name="description" rows="3"> <textarea id="description" name="description" rows={3}>
${feedConfig.description || ""}</textarea {feedConfig.description || ""}
> </textarea>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="allowed_senders" <label for="allowed_senders">
>Allowed senders (optional, one email or domain per Allowed senders (optional, one email or domain per line)
line)</label </label>
>
<textarea <textarea
id="allowed_senders" id="allowed_senders"
name="allowed_senders" name="allowed_senders"
rows="3" rows={3}
placeholder="newsletter@example.com&#10;techmeme.com" placeholder={"newsletter@example.com\ntechmeme.com"}
>
${(feedConfig.allowed_senders || []).join("\n")}</textarea
>
<small
>When set, inbound emails are only accepted from these
senders/domains.</small
> >
{(feedConfig.allowed_senders || []).join("\n")}
</textarea>
<small>
When set, inbound emails are only accepted from these
senders/domains.
</small>
</div> </div>
<input type="hidden" id="language" name="language" value="en" /> <input type="hidden" id="language" name="language" value="en" />
<button type="submit" class="button">Update Feed</button> <button type="submit" class="button">
Update Feed
</button>
</form> </form>
</div> </div>
</div> </div>
`, </Layout>,
),
); );
}); });