()
| 7 | import { DB_LOGOS } from '../../lib/db-logos'; |
| 8 | |
| 9 | export default function SourceDetailView() { |
| 10 | const { sourceId } = useParams<{ sourceId: string }>(); |
| 11 | const [source, setSource] = useState<DataSource | null>(null); |
| 12 | const [isLoading, setIsLoading] = useState(true); |
| 13 | const [error, setError] = useState<ApiError | null>(null); |
| 14 | |
| 15 | useEffect(() => { |
| 16 | if (!sourceId) return; |
| 17 | |
| 18 | setIsLoading(true); |
| 19 | setError(null); |
| 20 | |
| 21 | fetchSource(sourceId) |
| 22 | .then((data) => { |
| 23 | setSource(data); |
| 24 | setIsLoading(false); |
| 25 | }) |
| 26 | .catch((err: ApiError) => { |
| 27 | setError(err); |
| 28 | setIsLoading(false); |
| 29 | }); |
| 30 | }, [sourceId]); |
| 31 | |
| 32 | if (!sourceId) { |
| 33 | return <Navigate to="/" replace />; |
| 34 | } |
| 35 | |
| 36 | if (isLoading) { |
| 37 | return ( |
| 38 | <div className="container mx-auto px-8 py-12"> |
| 39 | <div className="text-muted-foreground">Loading source details...</div> |
| 40 | </div> |
| 41 | ); |
| 42 | } |
| 43 | |
| 44 | if (error) { |
| 45 | // If source not found, redirect to 404 page |
| 46 | if (error.status === 404) { |
| 47 | return <Navigate to="/404" replace />; |
| 48 | } |
| 49 | |
| 50 | // For other errors, show error message |
| 51 | return ( |
| 52 | <div className="container mx-auto px-8 py-12"> |
| 53 | <div className="bg-destructive/10 border border-destructive/20 rounded-lg p-6"> |
| 54 | <h2 className="text-lg font-semibold text-destructive mb-2">Error</h2> |
| 55 | <p className="text-destructive/90">{error.message}</p> |
| 56 | </div> |
| 57 | </div> |
| 58 | ); |
| 59 | } |
| 60 | |
| 61 | if (!source) { |
| 62 | return ( |
| 63 | <div className="container mx-auto px-8 py-12"> |
| 64 | <div className="text-muted-foreground">Source not found</div> |
| 65 | </div> |
| 66 | ); |
nothing calls this directly
no test coverage detected