Public Access
fix: wire up 'Send Vote Email' button to POST /admin/orchestrate/email
- Add triggerOrchestrate() to API client calling POST /admin/orchestrate/{step}
- Replace dead <button> in Dashboard with VoteEmailButton component:
onClick calls triggerOrchestrate('email'), shows toast spinner + success/error
This commit is contained in:
@@ -64,6 +64,7 @@ export const mealPlannerApi = {
|
|||||||
getMealPlans: (params?: any) => api.get('/admin/meal-plans', { params }),
|
getMealPlans: (params?: any) => api.get('/admin/meal-plans', { params }),
|
||||||
getStats: () => api.get('/admin/stats'),
|
getStats: () => api.get('/admin/stats'),
|
||||||
testEmail: (email: string) => api.post('/admin/test-email', null, { params: { email } }),
|
testEmail: (email: string) => api.post('/admin/test-email', null, { params: { email } }),
|
||||||
|
triggerOrchestrate: (step: string, weekStart?: string) => api.post(`/admin/orchestrate/${step}`, null, { params: { week_start: weekStart } }),
|
||||||
},
|
},
|
||||||
|
|
||||||
feedback: {
|
feedback: {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
import { useState } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { CookingPot, CalendarDays, ShoppingCart, ChevronRight, Sparkles } from 'lucide-react'
|
import { CookingPot, CalendarDays, ShoppingCart, ChevronRight, Sparkles, Loader2 } from 'lucide-react'
|
||||||
|
import toast from 'react-hot-toast'
|
||||||
import { mealPlannerApi } from '../api'
|
import { mealPlannerApi } from '../api'
|
||||||
import type { MealPlan, MealPlanItem } from '../types'
|
import type { MealPlan, MealPlanItem } from '../types'
|
||||||
import { Badge } from '../components/ui/Badge'
|
import { Badge } from '../components/ui/Badge'
|
||||||
@@ -129,6 +131,34 @@ function DashboardSkeleton() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function VoteEmailButton() {
|
||||||
|
const [sendingVoteEmail, setSendingVoteEmail] = useState(false)
|
||||||
|
|
||||||
|
async function handleSend() {
|
||||||
|
setSendingVoteEmail(true)
|
||||||
|
try {
|
||||||
|
const res = await mealPlannerApi.admin.triggerOrchestrate('email')
|
||||||
|
const status = res.data?.status || res.data?.message || 'sent'
|
||||||
|
toast.success(status)
|
||||||
|
} catch (err: any) {
|
||||||
|
toast.error(err?.response?.data?.detail || 'Failed to send vote emails')
|
||||||
|
} finally {
|
||||||
|
setSendingVoteEmail(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={handleSend}
|
||||||
|
disabled={sendingVoteEmail}
|
||||||
|
className="inline-flex items-center gap-1 text-sm font-medium text-primary-600 hover:text-primary-700 transition-colors text-left disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
>
|
||||||
|
{sendingVoteEmail ? <Loader2 className="w-4 h-4 animate-spin" /> : <ChevronRight className="w-4 h-4" />}
|
||||||
|
{sendingVoteEmail ? 'Sending...' : 'Send Vote Email'}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
const { data: mealPlan, isLoading } = useQuery<MealPlan | null>({
|
const { data: mealPlan, isLoading } = useQuery<MealPlan | null>({
|
||||||
queryKey: ['mealPlan'],
|
queryKey: ['mealPlan'],
|
||||||
@@ -251,9 +281,7 @@ export default function Dashboard() {
|
|||||||
>
|
>
|
||||||
Manage Pantry <ChevronRight className="w-4 h-4" />
|
Manage Pantry <ChevronRight className="w-4 h-4" />
|
||||||
</Link>
|
</Link>
|
||||||
<button className="inline-flex items-center gap-1 text-sm font-medium text-primary-600 hover:text-primary-700 transition-colors text-left">
|
<VoteEmailButton />
|
||||||
Send Vote Email <ChevronRight className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user