diff --git a/src/routes/admin/emails.ts b/src/routes/admin/emails.tsx similarity index 56% rename from src/routes/admin/emails.ts rename to src/routes/admin/emails.tsx index 1d06084..6b81a58 100644 --- a/src/routes/admin/emails.ts +++ b/src/routes/admin/emails.tsx @@ -1,5 +1,4 @@ import { Hono } from "hono"; -import { html, raw } from "hono/html"; import { Env, FeedConfig, @@ -8,13 +7,69 @@ import { EmailMetadata, } from "../../types"; import { logger } from "../../lib/logger"; -import { layout, clampText } from "./ui"; +import { Layout, clampText } from "./ui"; import { deleteKeysWithConcurrency } from "./helpers"; type AppEnv = { Bindings: Env }; export const emailsRouter = new Hono(); +// ── Shared SVG icons ────────────────────────────────────────────────────────── + +const CopyIcon = () => ( + + + + +); + +const CheckIcon = () => ( + + + +); + +type CopyFieldProps = { + label: string; + value: string; + display?: string; +}; + +const CopyField = ({ label, value, display }: CopyFieldProps) => ( +
+ {label} +
+ + {display ?? value} + +
+ + +
+
+
+); + // ── View all emails for a feed ──────────────────────────────────────────────── emailsRouter.get("/feeds/:feedId/emails", async (c) => { @@ -35,327 +90,11 @@ emailsRouter.get("/feeds/:feedId/emails", async (c) => { return c.text("Feed not found", 404); } - return c.html( - layout( - `${feedConfig.title} - Emails`, - html` -
-
-
-

${feedConfig.title} - Emails

-
- -
+ const emailAddress = `${feedId}@${env.DOMAIN}`; + const rssUrl = `https://${env.DOMAIN}/rss/${feedId}`; -
-

Feed Details

-
-
- Email Address: -
- ${feedId}@${env.DOMAIN} -
- - - - - - - -
-
-
-
- RSS Feed: -
- https://${env.DOMAIN}/rss/${feedId} -
- - - - - - - -
-
-
-
-
- -

- Emails (${feedMetadata.emails.length}) -

- - ${message === "bulkDeleted" - ? html`
-

Deleted ${Number.isFinite(count) ? count : 0} email(s).

-
` - : ""} - ${message === "bulkDeleteNoop" - ? html`

No emails were selected.

` - : ""} - ${feedMetadata.emails.length > 0 - ? html` -
-
-
- - Showing ${feedMetadata.emails.length} - 0 selected - - - -
-
- -
- - - - - - - - - - - - - - - - - ${feedMetadata.emails.map((email: EmailMetadata) => { - const subjectDisplay = clampText(email.subject, 180); - const subjectHover = clampText(email.subject, 1000); - const sortSubject = subjectHover.toLowerCase(); - const sortReceivedAt = String(email.receivedAt); - const searchHaystack = clampText( - email.subject, - 320, - ).toLowerCase(); - return html` - - - - - - - `; - })} - -
- - - -
-
- -
-
- Actions -
-
-
-
- ` - : html`
-

- No emails received yet. Subscribe to newsletters using the - email address above. -

-
`} -
- - - `, - ), + `; + + return c.html( + +
+
+
+

{feedConfig.title} - Emails

+
+ +
+ +
+

Feed Details

+
+ + +
+
+ +

+ Emails ( + {feedMetadata.emails.length}) +

+ + {message === "bulkDeleted" && ( +
+

Deleted {Number.isFinite(count) ? count : 0} email(s).

+
+ )} + {message === "bulkDeleteNoop" && ( +
+

No emails were selected.

+
+ )} + {feedMetadata.emails.length > 0 ? ( +
+
+
+ + + Showing {feedMetadata.emails.length} + + + 0 selected + + + + +
+
+ +
+ + + + + + + + + + + + + + + + + {feedMetadata.emails.map((email: EmailMetadata) => { + const subjectDisplay = clampText(email.subject, 180); + const subjectHover = clampText(email.subject, 1000); + const sortSubject = subjectHover.toLowerCase(); + const sortReceivedAt = String(email.receivedAt); + const searchHaystack = clampText( + email.subject, + 320, + ).toLowerCase(); + return ( + + + + + + + ); + })} + +
+ + + +
+
+ +
+
+ Actions +
+
+
+
+ ) : ( +
+

+ No emails received yet. Subscribe to newsletters using the email + address above. +

+
+ )} +
+ + - `, - ), + `; + + return c.html( + +
+
+
+

Email Content

+
+ +
+ +
+ + +
+ + +
+ + +
+
+ +