Fix: Resolve ESLint errors and remove duplicate useToast hook
This commit is contained in:
@ -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",
|
||||
}
|
||||
}
|
||||
];
|
||||
|
@ -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"
|
||||
|
@ -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';
|
||||
|
@ -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';
|
||||
|
@ -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'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'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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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 "Load Comparison".</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 "Load Comparison".</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 "Load Comparison" to view the chart.</p>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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't been traded internationally
|
||||
or if trade reports haven'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:
|
||||
|
@ -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>
|
||||
|
@ -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) => {
|
||||
|
@ -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}
|
||||
|
@ -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;
|
||||
|
@ -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 =
|
||||
| {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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 "{commonName?.name}" for <span className="italic">{species.scientific_name}</span>
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
@ -115,4 +115,4 @@ export default function EditCommonName() {
|
||||
</div>
|
||||
</AdminLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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 "{deleteDialog.commonName?.name}" ({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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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 {
|
||||
|
Reference in New Issue
Block a user