6W73J6WJQHALRF4ZLBJ4JH6H7UYT74KFPKFH537T2BRW2G6MCLRQC VFENVAHOAERPOWJFWVQBS5GXK3F422I62ZGQGOJHALKZZK2Q2SJAC NK2VPE6EAGQINWN6SIELRLOR3K7KTA3MSQLLDGKFVWU35LSB37RAC ONSQYCF6NFUEA24ORB62W4P62LKUMV7C5PLYRZQULHFDNROEY2HQC OBXY6BHNROIV7W4O3MMQ6GTQXU2XUKF5A4DCJYDLUEUHV72E7BNAC 4FBIL6IZUDNCXTM6EUHTEOJRHVI4LIIX4BU2IXPXKR362GKIAJMQC DBDM3MJDJ5TRAH3FA7MLBGFUPZF4GIM4CPZAKFNS5MV3UYQOAFNQC LSAQ6ZM2NELU3FIWKEFBOXKVLSZS2ZOK2PHPHJRWPVZ5CVILSUYQC description: item.description});
description: item.description,callTypes: []};acc[item.fileId].push(existingSpecies);}// Add call type if present and not already addedif (item.callTypeId && item.callTypeName) {const existingCallType = existingSpecies.callTypes.find(ct => ct.id === item.callTypeId);if (!existingCallType) {existingSpecies.callTypes.push({id: item.callTypeId,name: item.callTypeName});}
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./ui/table";import { Pagination } from "./ui/pagination";
import React, { useEffect, useState } from "react";import { File as FileType, FilesResponse, NightFilter, PaginationMetadata, SpeciesOption } from "../types";
'bg-stone-200 text-stone-800','bg-gray-200 text-gray-800','bg-slate-200 text-slate-800','bg-zinc-200 text-zinc-800','bg-neutral-200 text-neutral-800','bg-stone-300 text-stone-800','bg-gray-300 text-gray-800','bg-slate-300 text-slate-800'
"bg-stone-200 text-stone-800","bg-gray-200 text-gray-800","bg-slate-200 text-slate-800","bg-zinc-200 text-zinc-800","bg-neutral-200 text-neutral-800","bg-stone-300 text-stone-800","bg-gray-300 text-gray-800","bg-slate-300 text-slate-800",
'bg-stone-200 text-stone-800','bg-gray-200 text-gray-800','bg-slate-200 text-slate-800','bg-zinc-200 text-zinc-800','bg-neutral-200 text-neutral-800','bg-stone-300 text-stone-800','bg-gray-300 text-gray-800','bg-slate-300 text-slate-800'
"bg-stone-200 text-stone-800","bg-gray-200 text-gray-800","bg-slate-200 text-slate-800","bg-zinc-200 text-zinc-800","bg-neutral-200 text-neutral-800","bg-stone-300 text-stone-800","bg-gray-300 text-gray-800","bg-slate-300 text-slate-800",
// Generate different gray shades for call types (using darker grays to distinguish from species)const getCallTypeColor = (callTypeId: string): string => {const grayShades = ["bg-stone-200 text-stone-800","bg-gray-200 text-gray-800","bg-slate-200 text-slate-800","bg-zinc-200 text-zinc-800","bg-neutral-200 text-neutral-800","bg-stone-300 text-stone-800","bg-gray-300 text-gray-800","bg-slate-300 text-slate-800",];// Simple hash function to get consistent shade for each call typelet hash = 0;for (let i = 0; i < callTypeId.length; i++) {hash = ((hash << 5) - hash + callTypeId.charCodeAt(i)) & 0xffffffff;}return grayShades[Math.abs(hash) % grayShades.length];};
let url = '';if (apiEndpoint === 'files') {url = `/api/files?clusterId=${encodeURIComponent(requiredParams.clusterId!)}&page=${currentPage}&pageSize=100`;
let url = "";if (apiEndpoint === "files") {url = `/api/files?clusterId=${encodeURIComponent(requiredParams.clusterId!)}&page=${currentPage}&pageSize=100`;
url = `/api/selection?datasetId=${encodeURIComponent(requiredParams.datasetId!)}&speciesId=${encodeURIComponent(speciesFilter!)}&page=${currentPage}&pageSize=100`;
url = `/api/selection?datasetId=${encodeURIComponent(requiredParams.datasetId!)}&speciesId=${encodeURIComponent(speciesFilter!)}&page=${currentPage}&pageSize=100`;
const validMothMetadata = data.data.some(file =>file.mothMetadata &&(file.mothMetadata.gain !== null ||file.mothMetadata.batteryV !== null ||file.mothMetadata.tempC !== null)
const validMothMetadata = data.data.some(file =>file.mothMetadata&& (file.mothMetadata.gain !== null|| file.mothMetadata.batteryV !== null|| file.mothMetadata.tempC !== null)
const validSpeciesData = data.data.some(file =>file.species && file.species.length > 0
const validSpeciesData = data.data.some(file => file.species && file.species.length > 0);// Check if any files have call typesconst validCallTypesData = data.data.some(file =>file.species && file.species.some(species => species.callTypes && species.callTypes.length > 0)
}, [isAuthenticated, authLoading, getAccessToken, requiredParams.clusterId, requiredParams.datasetId, speciesFilter, currentPage, nightFilter, speciesOptions.length, apiEndpoint]);
}, [isAuthenticated,authLoading,getAccessToken,requiredParams.clusterId,requiredParams.datasetId,speciesFilter,currentPage,nightFilter,speciesOptions.length,apiEndpoint,]);
{hasSpecies && (<TableHead className="py-3 font-bold text-sm uppercase">Species</TableHead>)}
{hasSpecies && <TableHead className="py-3 font-bold text-sm uppercase">Species</TableHead>}{hasCallTypes && <TableHead className="py-3 font-bold text-sm uppercase">Call Types</TableHead>}
{files.length > 0 ? (files.map((file) => (<TableRow key={file.id} className={`hover:bg-primary/5 ${file.upload ? "bg-gray-50" : ""}`}><TableCellclassName="font-medium whitespace-normal break-words cursor-pointer"onClick={() => handleFileClick(file)}>{file.fileName}</TableCell><TableCellclassName="whitespace-normal break-words cursor-pointer"onClick={() => handleFileClick(file)}>{file.upload ? (<Check className="h-4 w-4 text-gray-600" />) : (<X className="h-4 w-4 text-gray-600" />)}</TableCell>{hasSpecies && (<TableCell
{files.length > 0? (files.map((file) => (<TableRow key={file.id} className={`hover:bg-primary/5 ${file.upload ? "bg-gray-50" : ""}`}><TableCellclassName="font-medium whitespace-normal break-words cursor-pointer"onClick={() =>handleFileClick(file)}>{file.fileName}</TableCell><TableCell
{file.species && file.species.length > 0 ? (<div className="flex flex-wrap gap-1">{file.species.map(species => (<divkey={species.id}className={`inline-block px-3 py-1 rounded-full text-xs font-medium ${getSpeciesColor(species.id)} text-center whitespace-nowrap overflow-hidden text-ellipsis`}style={{ minWidth: '80px', maxWidth: '150px' }}title={species.label}>{species.label}</div>))}</div>) : "—"}
{file.upload? <Check className="h-4 w-4 text-gray-600" />: <X className="h-4 w-4 text-gray-600" />}
{file.mothMetadata?.gain || "—"}
{file.species && file.species.length > 0? (<div className="flex flex-wrap gap-1">{file.species.map(species => (<divkey={species.id}className={`inline-block px-3 py-1 rounded-full text-xs font-medium ${getSpeciesColor(species.id)} text-center whitespace-nowrap overflow-hidden text-ellipsis`}style={{ minWidth: "80px", maxWidth: "150px" }}title={species.label}>{species.label}</div>))}</div>): "—"}
{formatBatteryVoltage(file.mothMetadata?.batteryV)}
{file.species && file.species.some(species =>species.callTypes && species.callTypes.length > 0)? (<div className="flex flex-wrap gap-1">{file.species.flatMap(species =>species.callTypes?.map(callType => (<divkey={`${species.id}-${callType.id}`}className={`inline-block px-3 py-1 rounded-full text-xs font-medium ${getCallTypeColor(callType.id)} text-center whitespace-nowrap overflow-hidden text-ellipsis`}style={{ minWidth: "80px", maxWidth: "150px" }}title={callType.name}>{callType.name}</div>)) || [])}</div>): "—"}
<TableCell
)}<TableCellclassName="whitespace-normal break-words cursor-pointer"onClick={() => handleFileClick(file)}>{formatDuration(Number(file.duration))}</TableCell>{hasMothMetadata && (<><TableCellclassName="whitespace-normal break-words cursor-pointer"onClick={() => handleFileClick(file)}>{file.mothMetadata?.gain || "—"}</TableCell><TableCellclassName="whitespace-normal break-words cursor-pointer"onClick={() => handleFileClick(file)}>{formatBatteryVoltage(file.mothMetadata?.batteryV)}</TableCell><TableCellclassName="whitespace-normal break-words cursor-pointer"onClick={() => handleFileClick(file)}>{formatTemperature(file.mothMetadata?.tempC)}</TableCell></>)}<TableCellclassName="whitespace-normal break-words cursor-pointer"onClick={() => handleFileClick(file)}>{formatMoonPhase(file.moonPhase)}</TableCell>{hasMetadata && (<TableCell
</>)}<TableCellclassName="whitespace-normal break-words cursor-pointer"onClick={() => handleFileClick(file)}
)}</TableRow>))): (<TableRow><TableCellcolSpan={// Calculate total columns based on optional columns4 // File, Status, Duration, Moon Phase (always present)+ (hasSpecies ? 1 : 0)+ (hasCallTypes ? 1 : 0)+ (hasMothMetadata ? 3 : 0)+ (hasMetadata ? 1 : 0)}className="text-center py-8"
))) : (<TableRow><TableCellcolSpan={// Calculate total columns based on optional columns4 + // File, Status, Duration, Moon Phase (always present)(hasSpecies ? 1 : 0) +(hasMothMetadata ? 3 : 0) +(hasMetadata ? 1 : 0)}className="text-center py-8"><div className="text-gray-500">{apiEndpoint === 'selection' ? "No files found with the selected filters" : "Try adjusting your filters to see more results"}</div></TableCell></TableRow>)}
)}
{!loading && !error && files.length === 0 && !pagination && speciesOptions.length === 0 && apiEndpoint === 'files' && (<div className="p-4 bg-yellow-50 text-yellow-800 rounded-md"><p className="font-medium">No files found for this cluster</p></div>)}
{!loading && !error && files.length === 0 && !pagination && speciesOptions.length === 0 && apiEndpoint === "files"&& (<div className="p-4 bg-yellow-50 text-yellow-800 rounded-md"><p className="font-medium">No files found for this cluster</p></div>)}