Add Tier 2 email features: AI draft, Calendar→Email announce, delivery status, engagement badges; v1.1.24
- EmailPage: useLocation to accept pre-filled compose from Calendar; delivery status pill on sent bubbles; AI Draft button calling ANTHROPIC_API_KEY endpoint - CalendarPage: useNavigate + announceEpisode(); ✉ Announce button in episode edit popover - ContactsPage: emailStatus types; unreplied badge; best delivery status pill; engagement score stars - server/routes/contact.js: POST /api/admin-contact-submissions/:id/draft-reply → Anthropic claude-haiku-4-5 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+14
-1
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
|
||||
// ── Types ────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -248,6 +248,7 @@ export default function CalendarShell() {
|
||||
// ── Calendar Client ───────────────────────────────────────────────────────────
|
||||
|
||||
function CalendarClient() {
|
||||
const navigate = useNavigate()
|
||||
const today = useMemo(() => new Date(), [])
|
||||
const [checklist, setChecklist] = useState<PodcastChecklistData | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
@@ -433,6 +434,17 @@ function CalendarClient() {
|
||||
setSelectedEpisodeId(null)
|
||||
}
|
||||
|
||||
function announceEpisode(ep: PodcastChecklistEpisode) {
|
||||
const parts = [ep.series, ep.episodeNumber ? `Episode ${ep.episodeNumber}` : null, ep.title].filter(Boolean)
|
||||
const subject = `New Episode: ${parts.join(' – ')}`
|
||||
const dateStr = ep.datePublished
|
||||
? new Date(ep.datePublished + 'T12:00:00').toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' })
|
||||
: ''
|
||||
const body = [parts.join(' – '), dateStr ? `Published ${dateStr}` : ''].filter(Boolean).join('\n\n')
|
||||
setEditEp(null)
|
||||
navigate('/email', { state: { compose: true, subject, body } })
|
||||
}
|
||||
|
||||
function openEdit(ep: PodcastChecklistEpisode) {
|
||||
setEditEp(ep)
|
||||
setEditForm({
|
||||
@@ -920,6 +932,7 @@ function CalendarClient() {
|
||||
</div>
|
||||
<div className="cal-popover-actions">
|
||||
<button type="button" className="em-btn em-btn--primary em-btn--sm" onClick={saveEdit}>Save</button>
|
||||
<button type="button" className="em-btn em-btn--ghost em-btn--sm" onClick={() => announceEpisode(editEp)} title="Open email compose pre-filled to announce this episode">✉ Announce</button>
|
||||
<button type="button" className="em-btn em-btn--ghost em-btn--sm" onClick={() => unschedule(editEp)}>Unschedule</button>
|
||||
<button type="button" className="em-btn em-btn--ghost em-btn--sm" onClick={() => setEditEp(null)}>Cancel</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user