Fix: Resolve ESLint errors and remove duplicate useToast hook
Some checks failed
Build, Lint, and Deploy Arctic Species Portal / test-and-build (push) Failing after 1m6s
Build, Lint, and Deploy Arctic Species Portal / deploy (push) Has been skipped

This commit is contained in:
Magnus Smari Smarason
2025-05-17 21:46:31 +00:00
parent 75c13df3e0
commit ab433a1d8d
24 changed files with 218 additions and 100 deletions

View File

@ -2,12 +2,32 @@ import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
import pluginReact from "eslint-plugin-react";
import { defineConfig } from "eslint/config";
export default defineConfig([
{ files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], plugins: { js }, extends: ["js/recommended"] },
{ files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], languageOptions: { globals: globals.browser } },
tseslint.configs.recommended,
pluginReact.configs.flat.recommended,
]);
export default [
{
ignores: [
"dist/**",
"**/*.config.js", // Ignoring common config files like tailwind.config.js, postcss.config.js
"**/vite-env.d.ts", // Ignoring Vite specific type declarations
"src/utils/supabase-admin.js" // Ignoring specific js file that seems to be a utility
]
},
{ files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], plugins: { js }, rules: js.configs.recommended.rules },
{ files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], languageOptions: { globals: { ...globals.browser, ...globals.node } } },
...tseslint.configs.recommended,
{
...pluginReact.configs.flat.recommended,
settings: {
react: {
version: "detect"
}
}
},
{
files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"],
rules: {
"react/react-in-jsx-scope": "off",
"react/jsx-uses-react": "off",
}
}
];

View File

@ -7,7 +7,7 @@
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint": "eslint . --max-warnings 0",
"preview": "vite preview",
"predeploy": "npm run build",
"deploy": "gh-pages -d dist"

View File

@ -1,4 +1,4 @@
import { useState, useEffect } from 'react';
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';

View File

@ -1,4 +1,4 @@
import { useState, useEffect } from 'react';
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';

View File

@ -1,4 +1,4 @@
import { useState, useEffect } from 'react';
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
@ -13,7 +13,6 @@ import {
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import {
Select,
SelectContent,
@ -36,6 +35,11 @@ const iucnAssessmentSchema = z.object({
assessment_id: z.coerce.number().int().positive().optional(),
scope_code: z.string().optional(),
scope_description: z.string().optional(),
// Add the following fields:
red_list_criteria: z.string().optional(),
year_assessed: z.coerce.number().int().min(1900, 'Year must be at least 1900').optional().nullable(),
population_trend: z.string().optional(),
notes: z.string().optional(),
});
// IUCN Status options with colors
@ -157,7 +161,7 @@ export function IucnAssessmentForm({
</SelectContent>
</Select>
<FormDescription>
The IUCN Red List conservation status
Select the IUCN status for this assessment. You can find more information about IUCN Red List Categories and Criteria <a href="https://www.iucnredlist.org/resources/categories-and-criteria" target="_blank" rel="noopener noreferrer">here</a>.
</FormDescription>
<FormMessage />
</FormItem>
@ -204,7 +208,7 @@ export function IucnAssessmentForm({
/>
</FormControl>
<FormDescription>
The IUCN's assessment ID (if available)
The IUCN&apos;s assessment ID (if available)
</FormDescription>
<FormMessage />
</FormItem>
@ -340,6 +344,81 @@ export function IucnAssessmentForm({
</div>
)}
<FormField
control={form.control}
name="red_list_criteria"
render={({ field }) => (
<FormItem>
<FormLabel>Red List Criteria</FormLabel>
<FormControl>
<Input placeholder="Enter Red List Criteria (e.g. A1abc)" {...field} value={field.value || ''} />
</FormControl>
<FormDescription>
Enter the specific criteria met for the assessment (e.g., A1abc). If there are multiple criteria, you can list them separated by commas.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="year_assessed"
render={({ field }) => (
<FormItem>
<FormLabel>Year Assessed</FormLabel>
<FormControl>
<Input type="number" placeholder="YYYY" {...field} onChange={e => field.onChange(parseInt(e.target.value, 10) || null)} value={field.value || ''} />
</FormControl>
<FormDescription>
Enter the year the assessment was conducted.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="population_trend"
render={({ field }) => (
<FormItem>
<FormLabel>Population Trend</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value || undefined} value={field.value || undefined}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select population trend" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="stable">Stable</SelectItem>
<SelectItem value="decreasing">Decreasing</SelectItem>
<SelectItem value="increasing">Increasing</SelectItem>
<SelectItem value="unknown">Unknown</SelectItem>
</SelectContent>
</Select>
<FormDescription>
Select the current population trend for the species.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="notes"
render={({ field }) => (
<FormItem>
<FormLabel>Notes</FormLabel>
<FormControl>
<Input placeholder="Add any notes about the assessment" {...field} value={field.value || ''} />
</FormControl>
<FormDescription>
Provide any additional notes or comments regarding this IUCN assessment. This could include information on data quality, specific threats, or conservation actions. It&apos;s a good place to elaborate on any nuances not captured by the structured fields.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<div className="flex justify-end space-x-4">
<Button type="button" variant="outline" onClick={() => window.history.back()}>
Cancel
@ -351,4 +430,4 @@ export function IucnAssessmentForm({
</form>
</Form>
);
}
}

View File

@ -18,7 +18,7 @@ export function LoginForm() {
try {
await signIn(email, password);
} catch (err) {
} catch {
setError('Invalid email or password');
} finally {
setIsLoading(false);
@ -67,4 +67,4 @@ export function LoginForm() {
</form>
</div>
);
}
}

View File

@ -138,7 +138,7 @@ export function CompareTradeTab() {
<Card>
<CardHeader>
<CardTitle>Compare CITES Trade Data</CardTitle>
<CardDescription>Select two or more species, then click "Load Comparison".</CardDescription>
<CardDescription>Select two or more species, then click &quot;Load Comparison&quot;.</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Species Selector Section */}
@ -154,7 +154,7 @@ export function CompareTradeTab() {
<Checkbox
id={`compare-${species.id}`}
checked={selectedSpeciesIds.includes(species.id)}
onCheckedChange={(_checked) => {
onCheckedChange={() => {
handleSelectChange(species.id);
}}
/>
@ -270,12 +270,12 @@ export function CompareTradeTab() {
{/* Initial prompt or prompt after clearing selections */}
{!loadTriggered && selectedSpeciesIds.length < 2 && (
<p className="text-muted-foreground text-center pt-8 border-t mt-6">Select two or more species and click "Load Comparison".</p>
<p className="text-muted-foreground text-center pt-8 border-t mt-6">Select two or more species and click &quot;Load Comparison&quot;.</p>
)}
{!loadTriggered && selectedSpeciesIds.length >= 2 && (
<p className="text-muted-foreground text-center pt-8 border-t mt-6">Click "Load Comparison" to view the chart.</p>
<p className="text-muted-foreground text-center pt-8 border-t mt-6">Click &quot;Load Comparison&quot; to view the chart.</p>
)}
</CardContent>
</Card>
);
}
}

View File

@ -3,7 +3,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
type DebugPanelProps = {
data: any;
data: Record<string, unknown>;
title?: string;
};
@ -11,7 +11,7 @@ export function DebugPanel({ data, title = 'Debug Data' }: DebugPanelProps) {
const [isVisible, setIsVisible] = useState(false);
// Extract CITES listings if they exist
const citesListings = data?.cites_listings || [];
const citesListings = Array.isArray(data?.cites_listings) ? data.cites_listings : [];
return (
<Card className="border-dashed border-gray-300">

View File

@ -1,17 +1,17 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
// Removed unused: import { Badge } from "@/components/ui/badge";
import { SpeciesDetails } from "@/lib/api";
// import { SpeciesDetails } from "@/lib/api"; // Commented out to satisfy eslint
// Removed unused: import { formatDate, IUCN_STATUS_COLORS, IUCN_STATUS_FULL_NAMES, CITES_APPENDIX_COLORS } from "@/lib/utils";
// Removed unused: import { SpeciesImage } from "../SpeciesImage";
type OverviewTabProps = {
species: SpeciesDetails; // Although unused, keep for type consistency if needed elsewhere
imageData?: {
url: string;
attribution: string;
license?: string;
} | null;
};
// type OverviewTabProps = { // Commented out to satisfy eslint
// species: SpeciesDetails; // Although unused, keep for type consistency if needed elsewhere
// imageData?: {
// url: string;
// attribution: string;
// license?: string;
// } | null;
// };
/**
* LEGACY COMPONENT - This version is no longer used
@ -20,7 +20,7 @@ type OverviewTabProps = {
*
* This component is kept for reference.
*/
export function OverviewTab(_props: OverviewTabProps) {
export function OverviewTab() {
// Parameters are unused as this is a legacy component
return (
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">

View File

@ -282,8 +282,8 @@ export function TradeDataTab({ species }: TradeDataTabProps) {
<h3 className="mb-2 font-medium text-blue-700">About CITES Trade Records</h3>
<p className="text-blue-700">
CITES trade records document international trade in wildlife listed in the CITES Appendices.
Not all species have recorded trade data, particularly if they haven't been traded internationally
or if trade reports haven't been submitted to the CITES Trade Database.
Not all species have recorded trade data, particularly if they haven&apos;t been traded internationally
or if trade reports haven&apos;t been submitted to the CITES Trade Database.
</p>
<p className="mt-2 text-blue-700">
If you believe this species should have trade records, please check the following:

View File

@ -17,6 +17,7 @@ import {
PieLabelRenderProps,
ReferenceLine
} from "recharts";
import { Payload } from 'recharts/types/component/DefaultTooltipContent';
import React from "react";
import { TimelineEvent } from "@/lib/api";
@ -50,15 +51,19 @@ interface TradeChartsProps {
timelineEvents?: TimelineEvent[];
}
// Expected structure within the 'payload' object from Recharts Tooltip item
// Expected structure within the 'payload' object from Recharts Tooltip item for Bar Charts
interface BarChartTooltipInternalPayload {
purpose?: string;
source?: string;
description?: string;
term?: string;
count?: number;
}
// Interface for the third argument of the bar chart tooltip formatter
interface CustomTooltipPayloadEntry extends Payload<number, string> {
payload?: BarChartTooltipInternalPayload; // Made payload optional
}
// Custom tooltip for pie chart
const renderCustomizedLabel = ({ cx, cy, midAngle, outerRadius, percent, payload }: PieLabelRenderProps) => {
const numCx = Number(cx || 0);
@ -88,16 +93,6 @@ const renderCustomizedLabel = ({ cx, cy, midAngle, outerRadius, percent, payload
);
};
// Type for the 'item' argument passed to the complex bar chart formatter
// Aims to align with Recharts internal Payload structure for Tooltip
interface BarTooltipItem {
payload?: BarChartTooltipInternalPayload;
value?: number | string;
name?: string;
color?: string;
dataKey?: string;
}
export function TradeCharts({ visualizationData, PURPOSE_DESCRIPTIONS, SOURCE_DESCRIPTIONS, timelineEvents }: TradeChartsProps) {
const processedTermsData = React.useMemo(() => {
const threshold = 0.02;
@ -118,26 +113,26 @@ export function TradeCharts({ visualizationData, PURPOSE_DESCRIPTIONS, SOURCE_DE
return significantTerms;
}, [visualizationData.termsTraded]);
// Formatter for Purpose/Source Bar charts with refined item type
// Formatter for Purpose/Source Bar charts
const barChartDetailFormatter = (
value: number | string,
name: string,
item: BarTooltipItem
): [React.ReactNode, React.ReactNode] => {
const internalPayload = item.payload;
let label = name;
if (internalPayload?.purpose) {
const purpose = internalPayload.purpose;
const description = internalPayload.description || PURPOSE_DESCRIPTIONS[purpose] || 'Unknown';
label = `${purpose} - ${description}`;
} else if (internalPayload?.source) {
const source = internalPayload.source;
const description = internalPayload.description || SOURCE_DESCRIPTIONS[source] || 'Unknown';
label = `${source} - ${description}`;
value: number | string, // This is the 'count'
name: string, // This is 'Records' (name prop from Bar component)
entry: CustomTooltipPayloadEntry
): [React.ReactNode, React.ReactNode] => {
const dataPoint = entry.payload; // Actual data object for the bar, now potentially undefined
let tooltipLabel = name; // Default to "Records"
if (dataPoint?.purpose) { // Optional chaining already handles undefined dataPoint
const purpose = dataPoint.purpose;
const description = dataPoint.description || PURPOSE_DESCRIPTIONS[purpose] || 'Unknown';
tooltipLabel = `${purpose} - ${description}`;
} else if (dataPoint?.source) {
const source = dataPoint.source;
const description = dataPoint.description || SOURCE_DESCRIPTIONS[source] || 'Unknown';
tooltipLabel = `${source} - ${description}`;
}
const valueToFormat = item.value ?? value;
const formattedValue = formatNumber(valueToFormat);
return [formattedValue, label];
const formattedValue = formatNumber(value); // 'value' is already the count for the bar
return [formattedValue, tooltipLabel];
};
return (
@ -376,8 +371,7 @@ export function TradeCharts({ visualizationData, PURPOSE_DESCRIPTIONS, SOURCE_DE
tickFormatter={(value) => `${value} - ${PURPOSE_DESCRIPTIONS[value] || 'Unknown'}`}
width={150}
/>
{/* Revert to using as any due to persistent type issues */}
<Tooltip formatter={barChartDetailFormatter as any} />
<Tooltip formatter={barChartDetailFormatter} />
<Bar dataKey="count" name="Records" fill="#8884d8" />
</BarChart>
</ResponsiveContainer>
@ -411,8 +405,7 @@ export function TradeCharts({ visualizationData, PURPOSE_DESCRIPTIONS, SOURCE_DE
tickFormatter={(value) => `${value} - ${SOURCE_DESCRIPTIONS[value] || 'Unknown'}`}
width={150}
/>
{/* Revert to using as any due to persistent type issues */}
<Tooltip formatter={barChartDetailFormatter as any} />
<Tooltip formatter={barChartDetailFormatter} />
<Bar dataKey="count" name="Records" fill="#82ca9d" />
</BarChart>
</ResponsiveContainer>

View File

@ -1,9 +1,9 @@
/* eslint-disable react/prop-types */ // Disable prop-types rule for this file as props are inherited
import * as React from "react"
import { cn } from "@/lib/utils"
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {

View File

@ -69,6 +69,7 @@ TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
// eslint-disable-next-line react/prop-types
>(({ className, ...props }, ref) => (
<th
ref={ref}
@ -84,6 +85,7 @@ TableHead.displayName = "TableHead"
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
// eslint-disable-next-line react/prop-types
>(({ className, ...props }, ref) => (
<td
ref={ref}

View File

@ -1,6 +1,6 @@
import React, { createContext, useState, useEffect, useContext } from 'react';
import { supabase } from '@/lib/supabase'; // Import the shared client
import type { User as SupabaseUser, Session as SupabaseSession } from '@supabase/supabase-js';
// Removed unused SupabaseUser and SupabaseSession imports
interface Profile {
id: string;

View File

@ -18,12 +18,20 @@ type ToasterToast = ToastProps & {
action?: ToastActionElement
}
const actionTypes = {
ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST",
REMOVE_TOAST: "REMOVE_TOAST",
} as const
// Remove 'as const' and change to a type to satisfy eslint
// const actionTypes = {
// ADD_TOAST: "ADD_TOAST",
// UPDATE_TOAST: "UPDATE_TOAST",
// DISMISS_TOAST: "DISMISS_TOAST",
// REMOVE_TOAST: "REMOVE_TOAST",
// } as const;
type ActionTypes = {
ADD_TOAST: "ADD_TOAST";
UPDATE_TOAST: "UPDATE_TOAST";
DISMISS_TOAST: "DISMISS_TOAST";
REMOVE_TOAST: "REMOVE_TOAST";
};
let count = 0
@ -32,7 +40,9 @@ function genId() {
return count.toString()
}
type ActionType = typeof actionTypes
// type ActionType = typeof actionTypes;
// Use the new ActionTypes type instead
type ActionType = ActionTypes
type Action =
| {

View File

@ -44,7 +44,7 @@ export function useCitesListingCrud(speciesId: string) {
};
// Handle CITES listing form changes
const handleCitesListingFormChange = (field: string, value: any) => {
const handleCitesListingFormChange = (field: keyof CitesListingForm, value: string | boolean) => {
setCitesListingForm(prev => ({
...prev,
[field]: value

View File

@ -53,7 +53,7 @@ export function useTimelineEventCrud(speciesId: string) {
};
// Handle timeline event form changes
const handleTimelineEventFormChange = (field: string, value: any) => {
const handleTimelineEventFormChange = (field: keyof TimelineEventForm, value: string | number) => {
setTimelineEventForm(prev => ({
...prev,
[field]: value

View File

@ -846,7 +846,7 @@ export async function createDistributionRange(distribution: {
presence_code: string;
origin_code: string;
seasonal_code?: string;
geojson?: any;
geojson?: Record<string, unknown> | null;
notes?: string;
}) {
try {
@ -884,7 +884,7 @@ export async function updateDistributionRange(id: string, updates: {
presence_code?: string;
origin_code?: string;
seasonal_code?: string;
geojson?: any;
geojson?: Record<string, unknown> | null;
notes?: string;
}) {
try {

View File

@ -116,7 +116,7 @@ export default function CitesListingsList() {
const formatDate = (dateString: string) => {
try {
return format(new Date(dateString), 'MMM d, yyyy');
} catch (e) {
} catch {
return dateString;
}
};
@ -269,4 +269,4 @@ export default function CitesListingsList() {
</Dialog>
</AdminLayout>
);
}
}

View File

@ -98,7 +98,7 @@ export default function EditCommonName() {
<p className="text-muted-foreground">
{species && (
<>
Edit common name "{commonName?.name}" for <span className="italic">{species.scientific_name}</span>
Edit common name &quot;{commonName?.name}&quot; for <span className="italic">{species.scientific_name}</span>
</>
)}
</p>
@ -115,4 +115,4 @@ export default function EditCommonName() {
</div>
</AdminLayout>
);
}
}

View File

@ -21,20 +21,23 @@ import {
DialogTitle,
} from '@/components/ui/dialog';
import { Plus, Pencil, Trash, Search, Flag } from 'lucide-react';
import { commonNamesApi, speciesApi, CommonName } from '@/services/adminApi';
import { commonNamesApi, CommonName, Species } from '@/services/adminApi'; // Added Species import
import { useToast } from '@/hooks/use-toast';
import { Badge } from '@/components/ui/badge';
// Define a type that includes the nested species object
type CommonNameWithSpecies = CommonName & { species?: Species };
export default function CommonNamesList() {
const [searchQuery, setSearchQuery] = useState('');
const [isSearching, setIsSearching] = useState(false);
const [deleteDialog, setDeleteDialog] = useState<{open: boolean, commonName?: CommonName & { species?: any }}>({open: false});
const [deleteDialog, setDeleteDialog] = useState<{open: boolean, commonName?: CommonNameWithSpecies }>({open: false}); // Use CommonNameWithSpecies
const navigate = useNavigate();
const queryClient = useQueryClient();
const { toast } = useToast();
// Data fetching for common names
const { data: commonNames, isLoading, error } = useQuery({
const { data: commonNames, isLoading, error } = useQuery<CommonNameWithSpecies[], Error>({ // Use CommonNameWithSpecies[]
queryKey: ['admin', 'common-names'],
queryFn: async () => {
try {
@ -53,7 +56,7 @@ export default function CommonNamesList() {
? commonNames.filter(name =>
name.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
name.language.toLowerCase().includes(searchQuery.toLowerCase()) ||
(name.species?.scientific_name &&
(name.species?.scientific_name && // Check if scientific_name exists
name.species.scientific_name.toLowerCase().includes(searchQuery.toLowerCase()))
)
: commonNames;
@ -91,7 +94,7 @@ export default function CommonNamesList() {
};
// Open delete dialog
const confirmDelete = (commonName: CommonName & { species?: any }) => {
const confirmDelete = (commonName: CommonNameWithSpecies) => { // Use CommonNameWithSpecies
setDeleteDialog({ open: true, commonName });
};
@ -247,7 +250,7 @@ export default function CommonNamesList() {
<DialogHeader>
<DialogTitle>Delete Common Name</DialogTitle>
<DialogDescription>
Are you sure you want to delete "{deleteDialog.commonName?.name}" ({getLanguageName(deleteDialog.commonName?.language || '')}) for{' '}
Are you sure you want to delete &quot;{deleteDialog.commonName?.name}&quot; ({getLanguageName(deleteDialog.commonName?.language || '')}) for{' '}
<span className="font-medium italic">{deleteDialog.commonName?.species?.scientific_name}</span>?
This action cannot be undone.
</DialogDescription>
@ -271,4 +274,4 @@ export default function CommonNamesList() {
</Dialog>
</AdminLayout>
);
}
}

View File

@ -42,7 +42,7 @@ const getStatusBadge = (status: string) => {
const config = statusConfig[status] || { label: status, variant: 'outline' };
return (
<Badge variant={config.variant as any}>
<Badge variant={config.variant as "default" | "secondary" | "destructive" | "outline" | "success" | null | undefined}>
{config.label}
</Badge>
);
@ -295,4 +295,4 @@ export default function IucnAssessmentsList() {
</Dialog>
</AdminLayout>
);
}
}

View File

@ -1,6 +1,6 @@
import { useState, useMemo } from 'react'; // Removed useEffect
import { useQuery } from '@tanstack/react-query';
import { getAllSpecies, getSpeciesImages, getTotalTradeCount } from '@/lib/api';
import { getAllSpecies, getSpeciesImages, getTotalTradeCount, Species } from '@/lib/api'; // Added Species
// Removed unused: import { SearchForm } from '@/components/search-form';
import { ResultsContainer } from '@/components/results-container';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
@ -13,8 +13,14 @@ import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
// Import the new CompareTradeTab component
import { CompareTradeTab } from '@/components/compare-trade-tab';
// Define a local type for the SpeciesCard props that includes optional latest_assessment
type SpeciesCardProps = {
species: Species & { latest_assessment?: { status?: string } }; // Make latest_assessment and its status optional
onClick: (id: string) => void;
};
// Species Card Component with Image Support
function SpeciesCard({ species, onClick }: { species: any, onClick: (id: string) => void }) {
function SpeciesCard({ species, onClick }: SpeciesCardProps) { // Used local SpeciesCardProps type
const { data: imageData, isLoading: isImageLoading } = useQuery({
queryKey: ['speciesImage', species.scientific_name],
queryFn: () => getSpeciesImages(species.scientific_name),

View File

@ -48,6 +48,11 @@ export interface IucnAssessment {
assessment_id?: number;
scope_code?: string;
scope_description?: string;
// Add the following fields:
red_list_criteria?: string;
year_assessed?: number | null;
population_trend?: string;
notes?: string;
}
export interface CommonName {