import React, { useState, useMemo, useEffect } from 'react'; import { Link, usePage, router } from '@inertiajs/react'; import { HardDrive, Plus, Trash2, Settings, RefreshCw, ChevronDown, ChevronUp, AlertCircle } from 'lucide-react'; import { EmptyState } from '../components/EmptyState'; import { SearchInput } from '../components/SearchInput'; import { Pagination } from '../components/Pagination'; import type { Datasource } from '../types/datasource'; import { Badge } from "@/components/ui/badge"; const ITEMS_PER_PAGE = 6; export default function DatasourcesPage({ datasources }: { datasources: Datasource[] }) { const { rootPath } = usePage().props; console.log(`rootPath: ${rootPath}`); const [searchQuery, setSearchQuery] = useState(''); const [currentPage, setCurrentPage] = useState(1); const [expandedErrors, setExpandedErrors] = useState([]); const filteredDatasources = useMemo(() => { return datasources.filter(source => source.name.toLowerCase().includes(searchQuery.toLowerCase()) || source.s3_bucket.toLowerCase().includes(searchQuery.toLowerCase()) ); }, [searchQuery, datasources]); const totalPages = Math.ceil(filteredDatasources.length / ITEMS_PER_PAGE); const paginatedDatasources = filteredDatasources.slice( (currentPage - 1) * ITEMS_PER_PAGE, currentPage * ITEMS_PER_PAGE ); const handleDelete = (datasourceId: number) => { if (confirm('Are you sure you want to delete this datasource? This action cannot be undone.')) { router.delete(`${rootPath}/datasources/${datasourceId}`); } }; const toggleError = (id: number) => { setExpandedErrors(prev => prev.includes(id) ? prev.filter(expandedId => expandedId !== id) : [...prev, id] ); }; const handleSync = async (id: number) => { try { router.post(`${rootPath}/datasources/${id}/sync`, {}, { preserveScroll: true, // Keeps the scroll position preserveState: true, // Keeps the form state onSuccess: (e) => { debugger; console.log("SUCCESS") // The page will automatically refresh with new data }, onError: () => { debugger; // Handle error case if needed console.error('Failed to sync datasource'); } }); } catch (error) { console.error('Failed to sync datasource:', error); } }; const formatLastSyncedAt = (lastSyncedAt: string) => { if (lastSyncedAt === 'Not Synced') return lastSyncedAt; const date = new Date(lastSyncedAt); return isNaN(date.getTime()) ? lastSyncedAt : date.toLocaleString(); }; useEffect(() => { let pollInterval: number | undefined; // Check if any datasource is syncing const isAnySyncing = datasources.some(d => d.is_syncing); if (isAnySyncing) { // Start polling every 5 seconds pollInterval = window.setInterval(() => { router.get(window.location.href, {}, { preserveScroll: true, preserveState: true, only: ['datasources'] }); }, 2000); } // Cleanup function return () => { if (pollInterval) { window.clearInterval(pollInterval); } }; }, [datasources]); if (datasources.length === 0) { return (
{ router.visit(`${rootPath}/datasources/new`) }} />
); } return (

Datasources

New Datasource
{paginatedDatasources.length === 0 ? (

No datasources found

No datasources match your search criteria. Try adjusting your search or add a new datasource.

New Datasource
) : ( <>
{paginatedDatasources.map((datasource) => (

{datasource.name}

{datasource.is_syncing ? ( syncing ) : datasource.sync_error ? ( sync error ) : datasource.last_synced_at !== 'Not Synced' ? ( synced ) : ( not synced )}

s3://{datasource.s3_bucket}/{datasource.s3_prefix}

Region

{datasource.s3_region}

Last Sync

{formatLastSyncedAt(datasource.last_synced_at)}

{datasource.sync_error && datasource.stacktrace && (
{expandedErrors.includes(datasource.id) && (
                            {datasource.stacktrace}
                          
)}
)}
))}
{totalPages > 1 && ( )} )}
); }