import React, { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Plus, Edit2, Trash2, ToggleLeft, ToggleRight, ExternalLink, CheckCircle, XCircle, Tag } from 'lucide-react'; import { apiClient, type Route, type RouteRequest, type Feed, type PlatformChannel } from '../lib/api'; import KeywordManager from '../components/KeywordManager'; const Routes: React.FC = () => { const [showCreateModal, setShowCreateModal] = useState(false); const [editingRoute, setEditingRoute] = useState(null); const queryClient = useQueryClient(); const { data: routes, isLoading, error } = useQuery({ queryKey: ['routes'], queryFn: () => apiClient.getRoutes(), }); const { data: feeds } = useQuery({ queryKey: ['feeds'], queryFn: () => apiClient.getFeeds(), }); const { data: onboardingOptions } = useQuery({ queryKey: ['onboarding-options'], queryFn: () => apiClient.getOnboardingOptions(), }); const toggleMutation = useMutation({ mutationFn: ({ feedId, channelId }: { feedId: number; channelId: number }) => apiClient.toggleRoute(feedId, channelId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['routes'] }); }, }); const deleteMutation = useMutation({ mutationFn: ({ feedId, channelId }: { feedId: number; channelId: number }) => apiClient.deleteRoute(feedId, channelId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['routes'] }); }, }); const createMutation = useMutation({ mutationFn: (data: RouteRequest) => apiClient.createRoute(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['routes'] }); setShowCreateModal(false); }, }); const updateMutation = useMutation({ mutationFn: ({ feedId, channelId, data }: { feedId: number; channelId: number; data: Partial }) => apiClient.updateRoute(feedId, channelId, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['routes'] }); setEditingRoute(null); }, }); const handleToggle = (route: Route) => { toggleMutation.mutate({ feedId: route.feed_id, channelId: route.platform_channel_id }); }; const handleDelete = (route: Route) => { if (confirm('Are you sure you want to delete this route?')) { deleteMutation.mutate({ feedId: route.feed_id, channelId: route.platform_channel_id }); } }; if (isLoading) { return (
{[...Array(3)].map((_, i) => (
))}
); } if (error) { return (

Failed to load routes

); } return (

Routes

Manage connections between your feeds and channels

{routes && routes.length > 0 ? ( routes.map((route: Route) => (

{route.feed?.name} → {route.platform_channel?.display_name || route.platform_channel?.name}

{route.is_active ? ( Active ) : ( Inactive )}
Priority: {route.priority} Feed: {route.feed?.name} Channel: {route.platform_channel?.display_name || route.platform_channel?.name} Created: {new Date(route.created_at).toLocaleDateString()}
{route.platform_channel?.description && (

{route.platform_channel.description}

)} {route.keywords && route.keywords.length > 0 && (
Keywords
{route.keywords.map((keyword) => ( {keyword.keyword} ))}
)} {(!route.keywords || route.keywords.length === 0) && (
No keyword filters - matches all articles
)}
)) ) : (

No routes

Get started by creating a new route to connect feeds with channels.

)}
{/* Create Route Modal */} {showCreateModal && ( setShowCreateModal(false)} onSubmit={(data) => createMutation.mutate(data)} isLoading={createMutation.isPending} /> )} {/* Edit Route Modal */} {editingRoute && ( setEditingRoute(null)} onSubmit={(data) => updateMutation.mutate({ feedId: editingRoute.feed_id, channelId: editingRoute.platform_channel_id, data })} isLoading={updateMutation.isPending} /> )}
); }; interface CreateRouteModalProps { feeds: Feed[]; channels: PlatformChannel[]; onClose: () => void; onSubmit: (data: RouteRequest) => void; isLoading: boolean; } const CreateRouteModal: React.FC = ({ feeds, channels, onClose, onSubmit, isLoading }) => { const [formData, setFormData] = useState({ feed_id: 0, platform_channel_id: 0, priority: 50, }); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); onSubmit(formData); }; return (
e.stopPropagation()}>

Create New Route

setFormData(prev => ({ ...prev, priority: parseInt(e.target.value) }))} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" />

Higher priority routes are processed first

); }; interface EditRouteModalProps { route: Route; onClose: () => void; onSubmit: (data: Partial) => void; isLoading: boolean; } const EditRouteModal: React.FC = ({ route, onClose, onSubmit, isLoading }) => { const [priority, setPriority] = useState(route.priority || 50); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); onSubmit({ priority }); }; return (
e.stopPropagation()} >

Edit Route

Feed: {route.feed?.name}

Channel: {route.platform_channel?.display_name || route.platform_channel?.name}

setPriority(parseInt(e.target.value))} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" />

Higher priority routes are processed first

{ // Keywords will be refreshed via React Query invalidation }} />
); }; export default Routes;